aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/libfyaml/src/lib/fy-doc.c
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2023-05-05 11:09:01 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2023-05-05 11:09:01 +0300
commitb5a989b16cafa8a3b3bc076f1097a0eda6f48c06 (patch)
tree4da744117a5aab37758921fa43b95a3068e5aec1 /contrib/libs/libfyaml/src/lib/fy-doc.c
parentfc1cffcfa7f0497a1f97b384a24bcbf23362f3be (diff)
downloadydb-b5a989b16cafa8a3b3bc076f1097a0eda6f48c06.tar.gz
Ydb stable 23-1-2623.1.26
x-stable-origin-commit: 22184a7e157553d447f17a2dffc4ea2d32dfd74d
Diffstat (limited to 'contrib/libs/libfyaml/src/lib/fy-doc.c')
-rw-r--r--contrib/libs/libfyaml/src/lib/fy-doc.c7377
1 files changed, 7377 insertions, 0 deletions
diff --git a/contrib/libs/libfyaml/src/lib/fy-doc.c b/contrib/libs/libfyaml/src/lib/fy-doc.c
new file mode 100644
index 0000000000..a40694a24c
--- /dev/null
+++ b/contrib/libs/libfyaml/src/lib/fy-doc.c
@@ -0,0 +1,7377 @@
+/*
+ * fy-doc.c - YAML document methods
+ *
+ * Copyright (c) 2019 Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
+#include <unistd.h>
+#endif
+
+#include <libfyaml.h>
+
+#include "fy-parse.h"
+#include "fy-doc.h"
+
+#include "fy-utils.h"
+
+#define XXH_STATIC_LINKING_ONLY
+#include "xxhash.h"
+
+static const struct fy_hash_desc hd_anchor;
+static const struct fy_hash_desc hd_nanchor;
+static const struct fy_hash_desc hd_mapping;
+
+int fy_node_hash_uint(struct fy_node *fyn, unsigned int *hashp);
+
+static struct fy_node *
+fy_node_by_path_internal(struct fy_node *fyn,
+ const char *path, size_t pathlen,
+ enum fy_node_walk_flags flags);
+
+#define FY_NODE_PATH_WALK_DEPTH_DEFAULT 16
+
+static inline unsigned int
+fy_node_walk_max_depth_from_flags(enum fy_node_walk_flags flags)
+{
+ unsigned int max_depth;
+
+ max_depth = ((unsigned int)flags >> FYNWF_MAXDEPTH_SHIFT) & FYNWF_MAXDEPTH_MASK;
+ if (max_depth == 0)
+ max_depth = FY_NODE_PATH_WALK_DEPTH_DEFAULT;
+
+ return max_depth;
+}
+
+static inline unsigned int
+fy_node_walk_marker_from_flags(enum fy_node_walk_flags flags)
+{
+ return ((unsigned int)flags >> FYNWF_MARKER_SHIFT) & FYNWF_MARKER_MASK;
+}
+
+/* internal simple key to optimize string lookups */
+static inline bool is_simple_key(const char *str, size_t len)
+{
+ const char *s, *e;
+ char c;
+
+ if (!str)
+ return false;
+
+ if (len == (size_t)-1)
+ len = strlen(str);
+
+ for (s = str, e = s + len; s < e; s++) {
+
+ c = *s;
+
+ /* note no isalpha() it's locale specific */
+ if (!((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_')))
+ break;
+ }
+
+ return s == e;
+}
+
+static void fy_resolve_parent_node(struct fy_document *fyd, struct fy_node *fyn, struct fy_node *fyn_parent);
+
+void fy_anchor_destroy(struct fy_anchor *fya)
+{
+ if (!fya)
+ return;
+ fy_token_unref(fya->anchor);
+ free(fya);
+}
+
+struct fy_anchor *fy_anchor_create(struct fy_document *fyd,
+ struct fy_node *fyn, struct fy_token *anchor)
+{
+ struct fy_anchor *fya = NULL;
+
+ fya = malloc(sizeof(*fya));
+ if (!fya)
+ return NULL;
+
+ fya->fyn = fyn;
+ fya->anchor = anchor;
+ fya->multiple = false;
+
+ return fya;
+}
+
+struct fy_anchor *fy_document_anchor_iterate(struct fy_document *fyd, void **prevp)
+{
+ struct fy_anchor_list *fyal;
+
+ if (!fyd || !prevp)
+ return NULL;
+
+ fyal = &fyd->anchors;
+
+ return *prevp = *prevp ? fy_anchor_next(fyal, *prevp) : fy_anchor_list_head(fyal);
+}
+
+#define FYDSAF_COPY FY_BIT(0)
+#define FYDSAF_MALLOCED FY_BIT(1)
+
+static int fy_document_set_anchor_internal(struct fy_document *fyd, struct fy_node *fyn, const char *text, size_t len,
+ unsigned int flags)
+{
+ const bool copy = !!(flags & FYDSAF_COPY);
+ const bool malloced = !!(flags & FYDSAF_MALLOCED);
+ struct fy_anchor *fya = NULL, *fyam = NULL;
+ struct fy_input *fyi = NULL;
+ struct fy_token *fyt = NULL;
+ struct fy_accel_entry *xle;
+ struct fy_atom handle;
+ char *data_copy = NULL;
+ const char *origtext;
+ size_t origlen;
+ int rc;
+
+ if (!fyd || !fyn || fyn->fyd != fyd)
+ return -1;
+
+ if (text && len == (size_t)-1)
+ len = strlen(text);
+
+ fya = fy_document_lookup_anchor_by_node(fyd, fyn);
+
+ if (!text) {
+ /* no anchor, and trying to delete? OK */
+ if (fya)
+ return 0;
+ /* remove the anchor */
+ fy_anchor_list_del(&fyd->anchors, fya);
+
+ if (fy_document_is_accelerated(fyd)) {
+ xle = fy_accel_entry_lookup_key_value(fyd->axl, fya->anchor, fya);
+ fy_accel_entry_remove(fyd->axl, xle);
+
+ xle = fy_accel_entry_lookup_key_value(fyd->naxl, fya->fyn, fya);
+ fy_accel_entry_remove(fyd->naxl, xle);
+ }
+
+ fy_anchor_destroy(fya);
+ return 0;
+ }
+
+ /* trying to add duplicate anchor */
+ if (fya) {
+ origtext = fy_token_get_text(fya->anchor, &origlen);
+ fyd_error_check(fyd, origtext, err_out,
+ "fy_token_get_text() failed");
+
+ FYD_NODE_ERROR(fyd, fyn, FYEM_DOC,
+ "cannot set anchor %.*s (anchor %.*s already exists)",
+ (int)len, text, (int)origlen, origtext);
+ if (malloced && text)
+ free((void *)text);
+ fya = NULL;
+ goto err_out;
+ }
+
+ if (copy) {
+ data_copy = malloc(len);
+ fyd_error_check(fyd, data_copy, err_out,
+ "malloc() failed");
+ memcpy(data_copy, text, len);
+ fyi = fy_input_from_malloc_data(data_copy, len, &handle, true);
+ } else if (malloced)
+ data_copy = (char *)text;
+ else
+ data_copy = NULL;
+
+ if (data_copy)
+ fyi = fy_input_from_malloc_data((void *)text, len, &handle, true);
+ else
+ fyi = fy_input_from_data(text, len, &handle, true);
+ fyd_error_check(fyd, fyi, err_out,
+ "fy_input_from_data() failed");
+ data_copy = NULL;
+
+ /* it must not be something funky */
+ if (!handle.valid_anchor)
+ goto err_out;
+
+ fyt = fy_token_create(FYTT_ANCHOR, &handle);
+ if (!fyt)
+ goto err_out;
+
+ fya = fy_anchor_create(fyd, fyn, fyt);
+ if (!fya)
+ goto err_out;
+
+ fy_anchor_list_add(&fyd->anchors, fya);
+ if (fy_document_is_accelerated(fyd)) {
+ xle = fy_accel_entry_lookup(fyd->axl, fya->anchor);
+ if (xle) {
+ fyam = (void *)xle->value;
+ /* multiple */
+ if (!fyam->multiple)
+ fyam->multiple = true;
+ fya->multiple = true;
+
+ fyd_notice(fyd, "register anchor %.*s is multiple", (int)len, text);
+ }
+
+ xle = fy_accel_entry_insert(fyd->axl, fya->anchor, fya);
+ fyd_error_check(fyd, xle, err_out,
+ "fy_accel_entry_insert() fyd->axl failed");
+ }
+
+ if (fy_document_is_accelerated(fyd)) {
+ rc = fy_accel_insert(fyd->naxl, fyn, fya);
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_accel_insert() fyd->naxl failed");
+ }
+
+ /* take away the input reference */
+ fy_input_unref(fyi);
+
+ return 0;
+err_out:
+ rc = -1;
+err_out_rc:
+ if (data_copy)
+ free(data_copy);
+ fy_anchor_destroy(fya);
+ fy_token_unref(fyt);
+ fy_input_unref(fyi);
+ fyd->diag->on_error = false;
+ return rc;
+}
+
+int fy_document_set_anchor(struct fy_document *fyd, struct fy_node *fyn, const char *text, size_t len)
+{
+ return fy_document_set_anchor_internal(fyd, fyn, text, len, 0);
+}
+
+int fy_node_set_anchor(struct fy_node *fyn, const char *text, size_t len)
+{
+ if (!fyn)
+ return -1;
+ return fy_document_set_anchor_internal(fyn->fyd, fyn, text, len, 0);
+}
+
+int fy_node_set_anchor_copy(struct fy_node *fyn, const char *text, size_t len)
+{
+ if (!fyn)
+ return -1;
+ return fy_document_set_anchor_internal(fyn->fyd, fyn, text, len, FYDSAF_COPY);
+}
+
+int fy_node_set_vanchorf(struct fy_node *fyn, const char *fmt, va_list ap)
+{
+ char *str;
+
+ if (!fyn || !fmt)
+ return -1;
+
+ alloca_vsprintf(&str, fmt, ap);
+ return fy_document_set_anchor_internal(fyn->fyd, fyn, str, FY_NT, FYDSAF_COPY);
+}
+
+int fy_node_set_anchorf(struct fy_node *fyn, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = fy_node_set_vanchorf(fyn, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+int fy_node_remove_anchor(struct fy_node *fyn)
+{
+ return fy_node_set_anchor(fyn, NULL, 0);
+}
+
+struct fy_anchor *fy_node_get_anchor(struct fy_node *fyn)
+{
+ if (!fyn)
+ return NULL;
+
+ return fy_document_lookup_anchor_by_node(fyn->fyd, fyn);
+}
+
+struct fy_anchor *fy_node_get_nearest_anchor(struct fy_node *fyn)
+{
+ struct fy_anchor *fya;
+ struct fy_node *fynt;
+
+ while ((fya = fy_node_get_anchor(fyn)) == NULL && (fynt = fy_node_get_parent(fyn)))
+ fyn = fynt;
+
+ return fya;
+}
+
+struct fy_node *fy_node_get_nearest_child_of(struct fy_node *fyn_base,
+ struct fy_node *fyn)
+{
+ struct fy_node *fynp;
+
+ if (!fyn)
+ return NULL;
+ if (!fyn_base)
+ fyn_base = fy_document_root(fy_node_document(fyn));
+ if (!fyn_base)
+ return NULL;
+
+ /* move up until we hit a node that's a child of fyn_base */
+ fynp = fyn;
+ while (fyn && (fynp = fy_node_get_parent(fyn)) != NULL && fyn_base != fynp)
+ fyn = fynp;
+
+ return fyn;
+}
+
+void fy_parse_document_destroy(struct fy_parser *fyp, struct fy_document *fyd)
+{
+ struct fy_node *fyn;
+ struct fy_anchor *fya;
+ struct fy_anchor *fyan;
+ struct fy_accel_entry *xle;
+
+ if (!fyd)
+ return;
+
+ fy_document_cleanup_path_expr_data(fyd);
+
+ fyn = fyd->root;
+ fyd->root = NULL;
+ fy_node_detach_and_free(fyn);
+
+ /* remove all anchors */
+ for (fya = fy_anchor_list_head(&fyd->anchors); fya; fya = fyan) {
+ fyan = fy_anchor_next(&fyd->anchors, fya);
+ fy_anchor_list_del(&fyd->anchors, fya);
+
+ if (fy_document_is_accelerated(fyd)) {
+ xle = fy_accel_entry_lookup_key_value(fyd->axl, fya->anchor, fya);
+ fy_accel_entry_remove(fyd->axl, xle);
+
+ xle = fy_accel_entry_lookup_key_value(fyd->naxl, fya->fyn, fya);
+ fy_accel_entry_remove(fyd->naxl, xle);
+ }
+
+ fy_anchor_destroy(fya);
+ }
+
+ if (fy_document_is_accelerated(fyd)) {
+ fy_accel_cleanup(fyd->axl);
+ free(fyd->axl);
+
+ fy_accel_cleanup(fyd->naxl);
+ free(fyd->naxl);
+ }
+
+ fy_document_state_unref(fyd->fyds);
+
+ fy_diag_unref(fyd->diag);
+
+ if (fyd->on_destroy_fn)
+ fyd->on_destroy_fn(fyd, fyd->userdata);
+
+ free(fyd);
+}
+
+struct fy_document *fy_parse_document_create(struct fy_parser *fyp, struct fy_eventp *fyep)
+{
+ struct fy_document *fyd = NULL;
+ struct fy_document_state *fyds;
+ struct fy_event *fye = NULL;
+ int rc;
+
+ if (!fyp || !fyep)
+ return NULL;
+
+ fye = &fyep->e;
+
+ FYP_TOKEN_ERROR_CHECK(fyp, fy_event_get_token(fye), FYEM_DOC,
+ fye->type == FYET_DOCUMENT_START, err_out,
+ "invalid start of event stream");
+
+ fyd = malloc(sizeof(*fyd));
+ fyp_error_check(fyp, fyd, err_out,
+ "malloc() failed");
+
+ memset(fyd, 0, sizeof(*fyd));
+
+ fyd->diag = fy_diag_ref(fyp->diag);
+ fyd->parse_cfg = fyp->cfg;
+
+ fy_anchor_list_init(&fyd->anchors);
+ if (fy_document_can_be_accelerated(fyd)) {
+ fyd->axl = malloc(sizeof(*fyd->axl));
+ fyp_error_check(fyp, fyd->axl, err_out,
+ "malloc() failed");
+
+ /* start with a very small bucket list */
+ rc = fy_accel_setup(fyd->axl, &hd_anchor, fyd, 8);
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_accel_setup() failed");
+
+ fyd->naxl = malloc(sizeof(*fyd->naxl));
+ fyp_error_check(fyp, fyd->axl, err_out,
+ "malloc() failed");
+
+ /* start with a very small bucket list */
+ rc = fy_accel_setup(fyd->naxl, &hd_nanchor, fyd, 8);
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_accel_setup() failed");
+ }
+
+ fyd->root = NULL;
+
+ fyds = fye->document_start.document_state;
+ fye->document_start.document_state = NULL;
+
+ /* and we're done with this event */
+ fy_parse_eventp_recycle(fyp, fyep);
+
+ /* drop the old reference */
+ fy_document_state_unref(fyd->fyds);
+
+ /* note that we keep the reference */
+ fyd->fyds = fyds;
+
+ fy_document_list_init(&fyd->children);
+
+ return fyd;
+
+err_out:
+ fy_parse_document_destroy(fyp, fyd);
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyd->diag->on_error = false;
+ return NULL;
+}
+
+const struct fy_parse_cfg *fy_document_get_cfg(struct fy_document *fyd)
+{
+ if (!fyd)
+ return NULL;
+ return &fyd->parse_cfg;
+}
+
+struct fy_diag *fy_document_get_diag(struct fy_document *fyd)
+{
+ if (!fyd || !fyd->diag)
+ return NULL;
+ return fy_diag_ref(fyd->diag);
+}
+
+int fy_document_set_diag(struct fy_document *fyd, struct fy_diag *diag)
+{
+ struct fy_diag_cfg dcfg;
+
+ if (!fyd)
+ return -1;
+
+ /* default? */
+ if (!diag) {
+ fy_diag_cfg_default(&dcfg);
+ diag = fy_diag_create(&dcfg);
+ if (!diag)
+ return -1;
+ }
+
+ fy_diag_unref(fyd->diag);
+ fyd->diag = fy_diag_ref(diag);
+
+ return 0;
+}
+
+struct fy_document *fy_node_document(struct fy_node *fyn)
+{
+ return fyn ? fyn->fyd : NULL;
+}
+
+static inline struct fy_anchor *
+fy_document_accel_lookup_anchor_by_token(struct fy_document *fyd, struct fy_token *fyt)
+{
+ assert(fyd);
+ assert(fyd->axl);
+ return (void *)fy_accel_lookup(fyd->axl, fyt);
+}
+
+static inline struct fy_anchor *
+fy_document_accel_lookup_anchor_by_node(struct fy_document *fyd, struct fy_node *fyn)
+{
+ assert(fyd);
+ assert(fyd->naxl);
+ return (void *)fy_accel_lookup(fyd->naxl, fyn);
+}
+
+static inline struct fy_node_pair *
+fy_node_accel_lookup_by_node(struct fy_node *fyn, struct fy_node *fyn_key)
+{
+ assert(fyn);
+ assert(fyn->xl);
+
+ return (void *)fy_accel_lookup(fyn->xl, (const void *)fyn_key);
+}
+
+struct fy_anchor *
+fy_document_lookup_anchor(struct fy_document *fyd, const char *anchor, size_t len)
+{
+ struct fy_anchor *fya;
+ struct fy_anchor_list *fyal;
+ struct fy_input *fyi;
+ struct fy_atom handle;
+ struct fy_token *fyt;
+ const char *text;
+ size_t text_len;
+
+ if (!fyd || !anchor)
+ return NULL;
+
+ if (len == (size_t)-1)
+ len = strlen(anchor);
+
+ if (fy_document_is_accelerated(fyd)) {
+ fyi = fy_input_from_data(anchor, len, &handle, true);
+ if (!fyi)
+ return NULL;
+
+ fyt = fy_token_create(FYTT_ANCHOR, &handle);
+
+ if (!fyt) {
+ fy_input_unref(fyi);
+ return NULL;
+ }
+
+ fya = fy_document_accel_lookup_anchor_by_token(fyd, fyt);
+
+ fy_input_unref(fyi);
+ fy_token_unref(fyt);
+
+ if (!fya)
+ return NULL;
+
+ /* single anchor? return it */
+ if (!fya->multiple)
+ return fya;
+
+ /* multiple anchors, fall-through */
+ }
+
+ /* note that we're performing the lookup in reverse creation order
+ * so that we pick the most recent
+ */
+ fyal = &fyd->anchors;
+ for (fya = fy_anchor_list_tail(fyal); fya; fya = fy_anchor_prev(fyal, fya)) {
+ text = fy_anchor_get_text(fya, &text_len);
+ if (!text)
+ return NULL;
+
+ if (len == text_len && !memcmp(anchor, text, len))
+ return fya;
+ }
+
+ return NULL;
+}
+
+struct fy_anchor *
+fy_document_lookup_anchor_by_token(struct fy_document *fyd,
+ struct fy_token *anchor)
+{
+ struct fy_anchor *fya, *fya_found, *fya_found2;
+ struct fy_anchor_list *fyal;
+ const char *anchor_text, *text;
+ size_t anchor_len, text_len;
+ int count;
+
+ if (!fyd || !anchor)
+ return NULL;
+
+ /* first try direct match (it's faster and the common case) */
+ if (fy_document_is_accelerated(fyd)) {
+ fya = fy_document_accel_lookup_anchor_by_token(fyd, anchor);
+ if (!fya)
+ return NULL;
+
+ /* single anchor? return it */
+ if (!fya->multiple)
+ return fya;
+
+ /* multiple anchors, fall-through */
+ }
+
+ anchor_text = fy_token_get_text(anchor, &anchor_len);
+ if (!anchor_text)
+ return NULL;
+
+ fyal = &fyd->anchors;
+
+ /* first pass, try with a single match */
+ count = 0;
+ fya_found = NULL;
+ for (fya = fy_anchor_list_head(fyal); fya; fya = fy_anchor_next(fyal, fya)) {
+ text = fy_anchor_get_text(fya, &text_len);
+ if (!text)
+ return NULL;
+
+ if (anchor_len == text_len && !memcmp(anchor_text, text, anchor_len)) {
+ count++;
+ fya_found = fya;
+ }
+ }
+
+ /* not found */
+ if (!count)
+ return NULL;
+
+ /* single one? fine */
+ if (count == 1)
+ return fya_found;
+
+ /* multiple ones, must pick the one that's the last one before
+ * the requesting token */
+ /* fyd_notice(fyd, "multiple anchors for %.*s", (int)anchor_len, anchor_text); */
+
+ /* only try the ones on the same input
+ * we don't try to cover the case where the label is referenced
+ * by other constructed documents
+ */
+ fya_found2 = NULL;
+ for (fya = fy_anchor_list_head(fyal); fya; fya = fy_anchor_next(fyal, fya)) {
+
+ /* only on the same input */
+ if (fy_token_get_input(fya->anchor) != fy_token_get_input(anchor))
+ continue;
+
+ text = fy_anchor_get_text(fya, &text_len);
+ if (!text)
+ return NULL;
+
+ if (anchor_len == text_len && !memcmp(anchor_text, text, anchor_len) &&
+ fy_token_start_pos(fya->anchor) < fy_token_start_pos(anchor)) {
+ fya_found2 = fya;
+ }
+ }
+
+ /* just return the one find earlier */
+ if (!fya_found2)
+ return fya_found;
+
+ /* return the one that was the latest */
+ return fya_found2;
+}
+
+struct fy_anchor *fy_document_lookup_anchor_by_node(struct fy_document *fyd, struct fy_node *fyn)
+{
+ struct fy_anchor *fya;
+ struct fy_anchor_list *fyal;
+
+ if (!fyd || !fyn)
+ return NULL;
+
+ if (fy_document_is_accelerated(fyd)) {
+ fya = fy_document_accel_lookup_anchor_by_node(fyd, fyn);
+ } else {
+ fyal = &fyd->anchors;
+ for (fya = fy_anchor_list_head(fyal); fya; fya = fy_anchor_next(fyal, fya)) {
+ if (fya->fyn == fyn)
+ break;
+ }
+ }
+
+ return fya;
+}
+
+const char *fy_anchor_get_text(struct fy_anchor *fya, size_t *lenp)
+{
+ if (!fya || !lenp)
+ return NULL;
+ return fy_token_get_text(fya->anchor, lenp);
+}
+
+struct fy_node *fy_anchor_node(struct fy_anchor *fya)
+{
+ if (!fya)
+ return NULL;
+ return fya->fyn;
+}
+
+int fy_node_pair_free(struct fy_node_pair *fynp)
+{
+ int rc, rc_ret = 0;
+
+ if (!fynp)
+ return 0;
+
+ rc = fy_node_free(fynp->key);
+ if (rc)
+ rc_ret = -1;
+ rc = fy_node_free(fynp->value);
+ if (rc)
+ rc_ret = -1;
+
+ free(fynp);
+
+ return rc_ret;
+}
+
+void fy_node_pair_detach_and_free(struct fy_node_pair *fynp)
+{
+ if (!fynp)
+ return;
+
+ fy_node_detach_and_free(fynp->key);
+ fy_node_detach_and_free(fynp->value);
+ free(fynp);
+}
+
+struct fy_node_pair *fy_node_pair_alloc(struct fy_document *fyd)
+{
+ struct fy_node_pair *fynp = NULL;
+
+ fynp = malloc(sizeof(*fynp));
+ if (!fynp)
+ return NULL;
+
+ fynp->key = NULL;
+ fynp->value = NULL;
+ fynp->fyd = fyd;
+ fynp->parent = NULL;
+ return fynp;
+}
+
+int fy_node_free(struct fy_node *fyn)
+{
+ struct fy_document *fyd;
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp;
+ struct fy_anchor *fya, *fyan;
+ struct fy_accel_entry_iter xli;
+ struct fy_accel_entry *xle, *xlen;
+
+ if (!fyn)
+ return 0;
+
+ /* a document must exist */
+ fyd = fyn->fyd;
+ if (!fyd)
+ return -1;
+
+ if (fyn->attached)
+ return -1;
+
+ if (fy_document_is_accelerated(fyd)) {
+ for (xle = fy_accel_entry_iter_start(&xli, fyd->naxl, fyn);
+ xle; xle = xlen) {
+ xlen = fy_accel_entry_iter_next(&xli);
+
+ fya = (void *)xle->value;
+
+ fy_anchor_list_del(&fyd->anchors, fya);
+
+ xle = fy_accel_entry_lookup_key_value(fyd->axl, fya->anchor, fya);
+ fy_accel_entry_remove(fyd->axl, xle);
+
+ xle = fy_accel_entry_lookup_key_value(fyd->naxl, fya->fyn, fya);
+ fy_accel_entry_remove(fyd->naxl, xle);
+
+ fy_anchor_destroy(fya);
+ }
+ fy_accel_entry_iter_finish(&xli);
+ } else {
+ /* remove anchors that are located on this node */
+ for (fya = fy_anchor_list_head(&fyd->anchors); fya; fya = fyan) {
+ fyan = fy_anchor_next(&fyd->anchors, fya);
+ if (fya->fyn == fyn) {
+ fy_anchor_list_del(&fyd->anchors, fya);
+ fy_anchor_destroy(fya);
+ }
+ }
+ }
+
+ /* clear the meta data of this node */
+ fy_node_clear_meta(fyn);
+
+ fy_token_unref(fyn->tag);
+ fyn->tag = NULL;
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ fy_token_unref(fyn->scalar);
+ fyn->scalar = NULL;
+ break;
+ case FYNT_SEQUENCE:
+ while ((fyni = fy_node_list_pop(&fyn->sequence)) != NULL)
+ fy_node_detach_and_free(fyni);
+ fy_token_unref(fyn->sequence_start);
+ fy_token_unref(fyn->sequence_end);
+ fyn->sequence_start = NULL;
+ fyn->sequence_end = NULL;
+ break;
+ case FYNT_MAPPING:
+ while ((fynp = fy_node_pair_list_pop(&fyn->mapping)) != NULL) {
+ if (fyn->xl)
+ fy_accel_remove(fyn->xl, fynp->key);
+ fy_node_pair_detach_and_free(fynp);
+ }
+ fy_token_unref(fyn->mapping_start);
+ fy_token_unref(fyn->mapping_end);
+ fyn->mapping_start = NULL;
+ fyn->mapping_end = NULL;
+ break;
+ }
+
+ if (fyn->xl) {
+ fy_accel_cleanup(fyn->xl);
+ free(fyn->xl);
+ }
+
+ fy_node_cleanup_path_expr_data(fyn);
+
+ free(fyn);
+
+ return 0;
+}
+
+void fy_node_detach_and_free(struct fy_node *fyn)
+{
+ int rc __FY_DEBUG_UNUSED__;
+
+ if (!fyn || !fyn->fyd)
+ return;
+
+ fyn->attached = false;
+
+ /* it must always succeed */
+ rc = fy_node_free(fyn);
+ assert(!rc);
+}
+
+struct fy_node *fy_node_alloc(struct fy_document *fyd, enum fy_node_type type)
+{
+ struct fy_node *fyn = NULL;
+ int rc;
+
+ fyn = malloc(sizeof(*fyn));
+ if (!fyn)
+ return NULL;
+
+ memset(fyn, 0, sizeof(*fyn));
+
+ fyn->style = FYNS_ANY;
+ fyn->fyd = fyd;
+ fyn->type = type;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ fy_node_list_init(&fyn->sequence);
+ break;
+ case FYNT_MAPPING:
+ fy_node_pair_list_init(&fyn->mapping);
+
+ if (fy_document_is_accelerated(fyd)) {
+ fyn->xl = malloc(sizeof(*fyn->xl));
+ fyd_error_check(fyd, fyn->xl, err_out,
+ "malloc() failed");
+
+ /* start with a very small bucket list */
+ rc = fy_accel_setup(fyn->xl, &hd_mapping, fyd, 8);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_accel_setup() failed");
+ }
+ break;
+ }
+ return fyn;
+
+err_out:
+ if (fyn) {
+ if (fyn->xl) {
+ fy_accel_cleanup(fyn->xl);
+ free(fyn->xl);
+ }
+ free(fyn);
+ }
+ return NULL;
+}
+
+struct fy_token *fy_node_non_synthesized_token(struct fy_node *fyn)
+{
+ struct fy_token *fyt_start = NULL, *fyt_end = NULL;
+ struct fy_token *fyt;
+ struct fy_input *fyi;
+ struct fy_atom handle;
+ unsigned int aflags;
+ const char *s, *e;
+ size_t size;
+
+ if (!fyn)
+ return NULL;
+
+ fyi = fy_node_get_input(fyn);
+ if (!fyi)
+ return NULL;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ return fy_token_ref(fyn->scalar);
+
+ case FYNT_SEQUENCE:
+ fyt_start = fyn->sequence_start;
+ fyt_end = fyn->sequence_end;
+ break;
+
+ case FYNT_MAPPING:
+ fyt_start = fyn->mapping_start;
+ fyt_end = fyn->mapping_end;
+ break;
+
+ }
+
+ if (!fyt_start || !fyt_end)
+ return NULL;
+
+ s = (char *)fy_input_start(fyi) + fyt_start->handle.start_mark.input_pos;
+ e = (char *)fy_input_start(fyi) + fyt_end->handle.end_mark.input_pos;
+ size = (size_t)(e - s);
+
+ if (size > 0)
+ aflags = fy_analyze_scalar_content(s, size,
+ fy_token_atom_json_mode(fyt_start),
+ fy_token_atom_lb_mode(fyt_start),
+ fy_token_atom_flow_ws_mode(fyt_start));
+ else
+ aflags = FYACF_EMPTY | FYACF_FLOW_PLAIN | FYACF_BLOCK_PLAIN;
+
+ memset(&handle, 0, sizeof(handle));
+ handle.start_mark = fyt_start->handle.start_mark;
+ handle.end_mark = fyt_end->handle.end_mark;
+
+ /* if it's plain, all is good */
+ if (aflags & FYACF_FLOW_PLAIN) {
+ handle.storage_hint = size; /* maximum */
+ handle.storage_hint_valid = false;
+ handle.direct_output = !!(aflags & FYACF_JSON_ESCAPE); /* direct only when no json escape */
+ handle.style = FYAS_PLAIN;
+ } else {
+ handle.storage_hint = 0; /* just calculate */
+ handle.storage_hint_valid = false;
+ handle.direct_output = false;
+ handle.style = FYAS_DOUBLE_QUOTED_MANUAL;
+ }
+ handle.empty = !!(aflags & FYACF_EMPTY);
+ handle.has_lb = !!(aflags & FYACF_LB);
+ handle.has_ws = !!(aflags & FYACF_WS);
+ handle.starts_with_ws = !!(aflags & FYACF_STARTS_WITH_WS);
+ handle.starts_with_lb = !!(aflags & FYACF_STARTS_WITH_LB);
+ handle.ends_with_ws = !!(aflags & FYACF_ENDS_WITH_WS);
+ handle.ends_with_lb = !!(aflags & FYACF_ENDS_WITH_LB);
+ handle.trailing_lb = !!(aflags & FYACF_TRAILING_LB);
+ handle.size0 = !!(aflags & FYACF_SIZE0);
+ handle.valid_anchor = !!(aflags & FYACF_VALID_ANCHOR);
+ handle.json_mode = false; /* always false */
+ handle.lb_mode = fylb_cr_nl; /* always \r\n */
+ handle.fws_mode = fyfws_space_tab; /* always space + tab */
+
+ handle.chomp = FYAC_STRIP;
+ handle.increment = 0;
+ handle.fyi = fyi;
+ handle.tabsize = 0;
+
+ fyt = fy_token_create(FYTT_INPUT_MARKER, &handle);
+ if (!fyt)
+ return NULL;
+
+ return fyt;
+}
+
+struct fy_token *fy_node_token(struct fy_node *fyn)
+{
+ struct fy_atom atom;
+ struct fy_input *fyi = NULL;
+ struct fy_token *fyt = NULL;
+ char *buf = NULL;
+
+ if (!fyn)
+ return NULL;
+
+ /* if it's non synthetic we can use the node extends */
+ if (!fy_node_is_synthetic(fyn))
+ return fy_node_non_synthesized_token(fyn);
+
+ /* emit to a string and create the token there */
+ buf = fy_emit_node_to_string(fyn, FYECF_MODE_FLOW_ONELINE | FYECF_WIDTH_INF);
+ if (!buf)
+ goto err_out;
+
+ fyi = fy_input_from_malloc_data(buf, FY_NT, &atom, true);
+ if (!fyi)
+ goto err_out;
+
+ fyt = fy_token_create(FYTT_INPUT_MARKER, &atom);
+ if (!fyt)
+ goto err_out;
+
+ /* take away the input reference */
+ fy_input_unref(fyi);
+
+ return fyt;
+
+err_out:
+ fy_input_unref(fyi);
+ if (buf)
+ free(buf);
+ return NULL;
+}
+
+bool fy_node_uses_single_input_only(struct fy_node *fyn, struct fy_input *fyi)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp;
+
+ if (!fyn || !fyi)
+ return false;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ return fy_token_get_input(fyn->scalar) == fyi;
+
+ case FYNT_SEQUENCE:
+ if (fy_token_get_input(fyn->sequence_start) != fyi)
+ return false;
+
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ if (!fy_node_uses_single_input_only(fyni, fyi))
+ return false;
+ }
+
+ if (fy_token_get_input(fyn->sequence_end) != fyi)
+ return false;
+ break;
+
+ case FYNT_MAPPING:
+ if (fy_token_get_input(fyn->mapping_start) != fyi)
+ return false;
+
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp;
+ fynp = fy_node_pair_next(&fyn->mapping, fynp)) {
+
+ if (fynp->key && !fy_node_uses_single_input_only(fynp->key, fyi))
+ return false;
+
+ if (fynp->value && !fy_node_uses_single_input_only(fynp->value, fyi))
+ return false;
+ }
+
+ if (fy_token_get_input(fyn->mapping_end) != fyi)
+ return false;
+
+ break;
+ }
+
+ return true;
+}
+
+struct fy_input *fy_node_get_first_input(struct fy_node *fyn)
+{
+ if (!fyn)
+ return NULL;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ return fy_token_get_input(fyn->scalar);
+
+ case FYNT_SEQUENCE:
+ return fy_token_get_input(fyn->sequence_start);
+
+ case FYNT_MAPPING:
+ return fy_token_get_input(fyn->mapping_start);
+ }
+
+ /* should never happen */
+ return NULL;
+}
+
+/* a node is synthetic if any of it's tokens reside in
+ * different inputs, or any sequence/mapping has been
+ * created via the manual sequence/mapping creation methods
+ */
+bool fy_node_is_synthetic(struct fy_node *fyn)
+{
+ return fyn && fyn->synthetic;
+}
+
+/* map this node and all of it's parents synthetic */
+void fy_node_mark_synthetic(struct fy_node *fyn)
+{
+ if (!fyn)
+ return;
+ fyn->synthetic = true;
+ while ((fyn = fy_node_get_document_parent(fyn)) != NULL)
+ fyn->synthetic = true;
+}
+
+struct fy_input *fy_node_get_input(struct fy_node *fyn)
+{
+ struct fy_input *fyi = NULL;
+
+ fyi = fy_node_get_first_input(fyn);
+ if (!fyi)
+ return NULL;
+
+ return fy_node_uses_single_input_only(fyn, fyi) ? fyi : NULL;
+}
+
+int fy_document_register_anchor(struct fy_document *fyd,
+ struct fy_node *fyn, struct fy_token *anchor)
+{
+ struct fy_anchor *fya, *fyam;
+ struct fy_accel_entry *xle;
+ const char *text;
+ size_t text_len;
+ int rc;
+
+ fya = fy_anchor_create(fyd, fyn, anchor);
+ fyd_error_check(fyd, fya, err_out,
+ "fy_anchor_create() failed");
+
+ fy_anchor_list_add_tail(&fyd->anchors, fya);
+ if (fy_document_is_accelerated(fyd)) {
+ xle = fy_accel_entry_lookup(fyd->axl, fya->anchor);
+ if (xle) {
+ fyam = (void *)xle->value;
+ /* multiple */
+ if (!fyam->multiple)
+ fyam->multiple = true;
+ fya->multiple = true;
+
+ text = fy_anchor_get_text(fya, &text_len);
+ fyd_notice(fyd, "register anchor %.*s is multiple", (int)text_len, text);
+ }
+
+ xle = fy_accel_entry_insert(fyd->axl, fya->anchor, fya);
+ fyd_error_check(fyd, xle, err_out,
+ "fy_accel_entry_insert() fyd->axl failed");
+ }
+
+ if (fy_document_is_accelerated(fyd)) {
+ rc = fy_accel_insert(fyd->naxl, fyn, fya);
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_accel_insert() fyd->naxl failed");
+ }
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ fyd->diag->on_error = false;
+ return rc;
+}
+
+struct fy_node_cmp_arg {
+ fy_node_scalar_compare_fn cmp_fn;
+ void *arg;
+};
+
+static int fy_node_scalar_cmp_default(struct fy_node *fyn_a,
+ struct fy_node *fyn_b,
+ void *arg);
+
+static int fy_node_mapping_sort_cmp_default(const struct fy_node_pair *fynp_a,
+ const struct fy_node_pair *fynp_b,
+ void *arg);
+
+bool fy_node_compare_user(struct fy_node *fyn1, struct fy_node *fyn2,
+ fy_node_mapping_sort_fn sort_fn, void *sort_fn_arg,
+ fy_node_scalar_compare_fn cmp_fn, void *cmp_fn_arg)
+{
+ struct fy_node *fyni1, *fyni2;
+ struct fy_node_pair *fynp1, *fynp2;
+ bool ret, null1, null2;
+ struct fy_node_pair **fynpp1, **fynpp2;
+ int i, count1, count2;
+ bool alias1, alias2;
+ struct fy_node_cmp_arg def_arg;
+
+ if (!cmp_fn) {
+ cmp_fn = fy_node_scalar_cmp_default;
+ cmp_fn_arg = NULL;
+ }
+ if (!sort_fn) {
+ sort_fn = fy_node_mapping_sort_cmp_default;
+ def_arg.cmp_fn = cmp_fn;
+ def_arg.arg = cmp_fn_arg;
+ sort_fn_arg = &def_arg;
+ } else {
+ def_arg.cmp_fn = NULL;
+ def_arg.arg = NULL;
+ }
+ /* equal pointers? */
+ if (fyn1 == fyn2)
+ return true;
+
+ null1 = !fyn1 || (fyn1->type == FYNT_SCALAR && fy_token_get_text_length(fyn1->scalar) == 0);
+ null2 = !fyn2 || (fyn2->type == FYNT_SCALAR && fy_token_get_text_length(fyn2->scalar) == 0);
+
+ /* both null */
+ if (null1 && null2)
+ return true;
+
+ /* either is NULL, no match */
+ if (null1 || null2)
+ return false;
+
+ /* types must match */
+ if (fyn1->type != fyn2->type)
+ return false;
+
+ ret = true;
+
+ switch (fyn1->type) {
+ case FYNT_SEQUENCE:
+ fyni1 = fy_node_list_head(&fyn1->sequence);
+ fyni2 = fy_node_list_head(&fyn2->sequence);
+ while (fyni1 && fyni2) {
+
+ ret = fy_node_compare(fyni1, fyni2);
+ if (!ret)
+ break;
+
+ fyni1 = fy_node_next(&fyn1->sequence, fyni1);
+ fyni2 = fy_node_next(&fyn2->sequence, fyni2);
+ }
+ if (ret && fyni1 != fyni2 && (!fyni1 || !fyni2))
+ ret = false;
+
+ break;
+
+ case FYNT_MAPPING:
+ count1 = fy_node_mapping_item_count(fyn1);
+ count2 = fy_node_mapping_item_count(fyn2);
+
+ /* mapping counts must match */
+ if (count1 != count2) {
+ ret = false;
+ break;
+ }
+
+ fynpp1 = FY_ALLOCA(sizeof(*fynpp1) * (count1 + 1));
+ fy_node_mapping_fill_array(fyn1, fynpp1, count1);
+ fy_node_mapping_perform_sort(fyn1, sort_fn, sort_fn_arg, fynpp1, count1);
+
+ fynpp2 = FY_ALLOCA(sizeof(*fynpp2) * (count2 + 1));
+ fy_node_mapping_fill_array(fyn2, fynpp2, count2);
+ fy_node_mapping_perform_sort(fyn2, sort_fn, sort_fn_arg, fynpp2, count2);
+
+ for (i = 0; i < count1; i++) {
+ fynp1 = fynpp1[i];
+ fynp2 = fynpp2[i];
+
+ ret = fy_node_compare(fynp1->key, fynp2->key);
+ if (!ret)
+ break;
+
+ ret = fy_node_compare(fynp1->value, fynp2->value);
+ if (!ret)
+ break;
+ }
+ if (i >= count1)
+ ret = true;
+
+ break;
+
+ case FYNT_SCALAR:
+ alias1 = fy_node_is_alias(fyn1);
+ alias2 = fy_node_is_alias(fyn2);
+
+ /* either both must be aliases or both not */
+ if (alias1 != alias2)
+ return false;
+
+ ret = !cmp_fn(fyn1, fyn2, cmp_fn_arg);
+ break;
+ }
+
+ return ret;
+}
+
+bool fy_node_compare(struct fy_node *fyn1, struct fy_node *fyn2)
+{
+ return fy_node_compare_user(fyn1, fyn2, NULL, NULL, NULL, NULL);
+}
+
+bool fy_node_compare_string(struct fy_node *fyn, const char *str, size_t len)
+{
+ struct fy_document *fyd = NULL;
+ bool ret;
+
+ fyd = fy_document_build_from_string(NULL, str, len);
+ if (!fyd)
+ return false;
+
+ ret = fy_node_compare(fyn, fy_document_root(fyd));
+
+ fy_document_destroy(fyd);
+
+ return ret;
+}
+
+bool fy_node_compare_token(struct fy_node *fyn, struct fy_token *fyt)
+{
+ /* check if there's NULL */
+ if (!fyn || !fyt)
+ return false;
+
+ /* only valid for scalars */
+ if (!fy_node_is_scalar(fyn) || fyt->type != FYTT_SCALAR)
+ return false;
+
+ return fy_token_cmp(fyn->scalar, fyt) == 0;
+}
+
+bool fy_node_compare_text(struct fy_node *fyn, const char *text, size_t len)
+{
+ const char *textn;
+ size_t lenn;
+
+ if (!fyn || !text)
+ return false;
+
+ textn = fy_node_get_scalar(fyn, &lenn);
+ if (!textn)
+ return false;
+
+ if (len == FY_NT)
+ len = strlen(text);
+
+ if (len != lenn)
+ return false;
+
+ return memcmp(text, textn, len) == 0;
+}
+
+struct fy_node_pair *fy_node_mapping_lookup_pair(struct fy_node *fyn, struct fy_node *fyn_key)
+{
+ struct fy_node_pair *fynpi, *fynp;
+
+ /* sanity check */
+ if (!fy_node_is_mapping(fyn))
+ return NULL;
+
+ fynp = NULL;
+
+
+ if (fyn->xl) {
+ fynp = fy_node_accel_lookup_by_node(fyn, fyn_key);
+ } else {
+ for (fynpi = fy_node_pair_list_head(&fyn->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fyn->mapping, fynpi)) {
+ if (fy_node_compare(fynpi->key, fyn_key)) {
+ fynp = fynpi;
+ break;
+ }
+ }
+ }
+
+ return fynp;
+}
+
+int fy_node_mapping_get_pair_index(struct fy_node *fyn, const struct fy_node_pair *fynp)
+{
+ struct fy_node_pair *fynpi;
+ int i;
+
+ for (i = 0, fynpi = fy_node_pair_list_head(&fyn->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fyn->mapping, fynpi), i++) {
+
+ if (fynpi == fynp)
+ return i;
+ }
+
+ return -1;
+}
+
+bool fy_node_mapping_key_is_duplicate(struct fy_node *fyn, struct fy_node *fyn_key)
+{
+ return fy_node_mapping_lookup_pair(fyn, fyn_key) != NULL;
+}
+
+static int
+fy_parse_document_load_node(struct fy_parser *fyp, struct fy_document *fyd,
+ struct fy_eventp *fyep, struct fy_node **fynp,
+ int *depthp);
+
+int fy_parse_document_load_alias(struct fy_parser *fyp, struct fy_document *fyd, struct fy_eventp *fyep, struct fy_node **fynp)
+{
+ *fynp = NULL;
+
+ fyp_doc_debug(fyp, "in %s", __func__);
+
+ /* TODO verify aliases etc */
+ fy_parse_eventp_recycle(fyp, fyep);
+ return 0;
+}
+
+static int
+fy_parse_document_load_scalar(struct fy_parser *fyp, struct fy_document *fyd,
+ struct fy_eventp *fyep, struct fy_node **fynp,
+ int *depthp)
+{
+ struct fy_node *fyn = NULL;
+ struct fy_event *fye;
+ int rc;
+
+ if (!fyd)
+ return -1;
+
+ fyp_error_check(fyp, fyep || !fyp->stream_error, err_out,
+ "no event to process");
+
+ FYP_PARSE_ERROR_CHECK(fyp, 0, 0, FYEM_DOC,
+ fyep, err_out,
+ "premature end of event stream");
+
+ fyp_doc_debug(fyp, "in %s [%s]", __func__, fy_event_type_txt[fyep->e.type]);
+
+ *fynp = NULL;
+
+ fye = &fyep->e;
+
+ /* we don't free nodes that often, so no need for recycling */
+ fyn = fy_node_alloc(fyd, FYNT_SCALAR);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_alloc() failed");
+
+ if (fye->type == FYET_SCALAR) {
+
+ /* move the tags and value to the node */
+ if (fye->scalar.value)
+ fyn->style = fy_node_style_from_scalar_style(fye->scalar.value->scalar.style);
+ else
+ fyn->style = FYNS_PLAIN;
+ fyn->tag = fye->scalar.tag;
+ fye->scalar.tag = NULL;
+
+ fyn->scalar = fye->scalar.value;
+ fye->scalar.value = NULL;
+
+ if (fye->scalar.anchor) {
+ rc = fy_document_register_anchor(fyd, fyn, fye->scalar.anchor);
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_document_register_anchor() failed");
+ fye->scalar.anchor = NULL;
+ }
+
+ } else if (fye->type == FYET_ALIAS) {
+ fyn->style = FYNS_ALIAS;
+ fyn->scalar = fye->alias.anchor;
+ fye->alias.anchor = NULL;
+ } else
+ assert(0);
+
+ *fynp = fyn;
+ fyn = NULL;
+
+ /* everything OK */
+ fy_parse_eventp_recycle(fyp, fyep);
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyd->diag->on_error = false;
+ return rc;
+}
+
+static int
+fy_parse_document_load_sequence(struct fy_parser *fyp, struct fy_document *fyd,
+ struct fy_eventp *fyep, struct fy_node **fynp,
+ int *depthp)
+{
+ struct fy_node *fyn = NULL, *fyn_item = NULL;
+ struct fy_event *fye = NULL;
+ struct fy_token *fyt_ss = NULL;
+ int rc;
+
+ fyp_error_check(fyp, fyep || !fyp->stream_error, err_out,
+ "no event to process");
+
+ FYP_PARSE_ERROR_CHECK(fyp, 0, 0, FYEM_DOC,
+ fyep, err_out,
+ "premature end of event stream");
+
+ fyp_doc_debug(fyp, "in %s [%s]", __func__, fy_event_type_txt[fyep->e.type]);
+
+ *fynp = NULL;
+
+ fye = &fyep->e;
+
+ fyt_ss = fye->sequence_start.sequence_start;
+
+ /* we don't free nodes that often, so no need for recycling */
+ fyn = fy_node_alloc(fyd, FYNT_SEQUENCE);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_alloc() failed");
+
+ fyn->style = fyt_ss && fyt_ss->type == FYTT_FLOW_SEQUENCE_START ? FYNS_FLOW : FYNS_BLOCK;
+
+ fyn->tag = fye->sequence_start.tag;
+ fye->sequence_start.tag = NULL;
+
+ if (fye->sequence_start.anchor) {
+ rc = fy_document_register_anchor(fyd, fyn, fye->sequence_start.anchor);
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_document_register_anchor() failed");
+ fye->sequence_start.anchor = NULL;
+ }
+
+ if (fye->sequence_start.sequence_start) {
+ fyn->sequence_start = fye->sequence_start.sequence_start;
+ fye->sequence_start.sequence_start = NULL;
+ } else
+ fyn->sequence_start = NULL;
+
+ assert(fyn->sequence_start);
+
+ /* done with this */
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyep = NULL;
+
+ while ((fyep = fy_parse_private(fyp)) != NULL) {
+ fye = &fyep->e;
+ if (fye->type == FYET_SEQUENCE_END)
+ break;
+
+ rc = fy_parse_document_load_node(fyp, fyd, fyep, &fyn_item, depthp);
+ fyep = NULL;
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_parse_document_load_node() failed");
+
+ fy_node_list_add_tail(&fyn->sequence, fyn_item);
+ fyn_item->attached = true;
+ fyn_item = NULL;
+ }
+
+ if (!fyep)
+ goto err_out;
+
+ if (fye->sequence_end.sequence_end) {
+ fyn->sequence_end = fye->sequence_end.sequence_end;
+ fye->sequence_end.sequence_end = NULL;
+ } else
+ fyn->sequence_end = NULL;
+
+ assert(fyn->sequence_end);
+
+ *fynp = fyn;
+ fyn = NULL;
+
+ fy_parse_eventp_recycle(fyp, fyep);
+ return 0;
+
+ /* fallthrough */
+err_out:
+ rc = -1;
+err_out_rc:
+ fy_parse_eventp_recycle(fyp, fyep);
+ fy_node_detach_and_free(fyn_item);
+ fy_node_detach_and_free(fyn);
+ return rc;
+}
+
+static int
+fy_parse_document_load_mapping(struct fy_parser *fyp, struct fy_document *fyd,
+ struct fy_eventp *fyep, struct fy_node **fynp,
+ int *depthp)
+{
+ struct fy_node *fyn = NULL, *fyn_key = NULL, *fyn_value = NULL;
+ struct fy_node_pair *fynp_item = NULL;
+ struct fy_event *fye = NULL;
+ struct fy_token *fyt_ms = NULL;
+ bool duplicate;
+ int rc;
+
+ fyp_error_check(fyp, fyep || !fyp->stream_error, err_out,
+ "no event to process");
+
+ FYP_PARSE_ERROR_CHECK(fyp, 0, 0, FYEM_DOC,
+ fyep, err_out,
+ "premature end of event stream");
+
+ fyp_doc_debug(fyp, "in %s [%s]", __func__, fy_event_type_txt[fyep->e.type]);
+
+ *fynp = NULL;
+
+ fye = &fyep->e;
+
+ fyt_ms = fye->mapping_start.mapping_start;
+
+ /* we don't free nodes that often, so no need for recycling */
+ fyn = fy_node_alloc(fyd, FYNT_MAPPING);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_alloc() failed");
+
+ fyn->style = fyt_ms && fyt_ms->type == FYTT_FLOW_MAPPING_START ? FYNS_FLOW : FYNS_BLOCK;
+
+ fyn->tag = fye->mapping_start.tag;
+ fye->mapping_start.tag = NULL;
+
+ if (fye->mapping_start.anchor) {
+ rc = fy_document_register_anchor(fyd, fyn, fye->mapping_start.anchor);
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_document_register_anchor() failed");
+ fye->mapping_start.anchor = NULL;
+ }
+
+ if (fye->mapping_start.mapping_start) {
+ fyn->mapping_start = fye->mapping_start.mapping_start;
+ fye->mapping_start.mapping_start = NULL;
+ }
+
+ assert(fyn->mapping_start);
+
+ /* done with this */
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyep = NULL;
+
+ while ((fyep = fy_parse_private(fyp)) != NULL) {
+ fye = &fyep->e;
+ if (fye->type == FYET_MAPPING_END)
+ break;
+
+ fynp_item = fy_node_pair_alloc(fyd);
+ fyp_error_check(fyp, fynp_item, err_out,
+ "fy_node_pair_alloc() failed");
+
+ fyn_key = NULL;
+ fyn_value = NULL;
+
+ rc = fy_parse_document_load_node(fyp, fyd,
+ fyep, &fyn_key, depthp);
+ fyep = NULL;
+
+ assert(fyn_key);
+
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_parse_document_load_node() failed");
+
+ /* if we don't allow duplicate keys */
+ if (!(fyd->parse_cfg.flags & FYPCF_ALLOW_DUPLICATE_KEYS)) {
+
+ /* make sure we don't add an already existing key */
+ duplicate = fy_node_mapping_key_is_duplicate(fyn, fyn_key);
+
+ FYP_NODE_ERROR_CHECK(fyp, fyn_key, FYEM_DOC,
+ !duplicate, err_out,
+ "duplicate key");
+ }
+
+ fyep = fy_parse_private(fyp);
+
+ fyp_error_check(fyp, fyep || !fyp->stream_error, err_out,
+ "fy_parse_private() failed");
+
+ FYP_PARSE_ERROR_CHECK(fyp, 0, 0, FYEM_DOC,
+ fyep, err_out,
+ "missing mapping value");
+
+ fye = &fyep->e;
+
+ rc = fy_parse_document_load_node(fyp, fyd,
+ fyep, &fyn_value, depthp);
+ fyep = NULL;
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_parse_document_load_node() failed");
+
+ assert(fyn_value);
+
+ fynp_item->key = fyn_key;
+ fynp_item->value = fyn_value;
+ fy_node_pair_list_add_tail(&fyn->mapping, fynp_item);
+ if (fyn->xl) {
+ rc = fy_accel_insert(fyn->xl, fynp_item->key, fynp_item);
+ fyp_error_check(fyp, !rc, err_out_rc,
+ "fy_accel_insert() failed");
+ }
+
+ if (fynp_item->key)
+ fynp_item->key->attached = true;
+ if (fynp_item->value)
+ fynp_item->value->attached = true;
+ fynp_item = NULL;
+ fyn_key = NULL;
+ fyn_value = NULL;
+ }
+
+ if (!fyep)
+ goto err_out;
+
+ if (fye->mapping_end.mapping_end) {
+ fyn->mapping_end = fye->mapping_end.mapping_end;
+ fye->mapping_end.mapping_end = NULL;
+ }
+
+ assert(fyn->mapping_end);
+
+ *fynp = fyn;
+ fyn = NULL;
+
+ fy_parse_eventp_recycle(fyp, fyep);
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ fy_parse_eventp_recycle(fyp, fyep);
+ fy_node_pair_free(fynp_item);
+ fy_node_detach_and_free(fyn_key);
+ fy_node_detach_and_free(fyn_value);
+ fy_node_detach_and_free(fyn);
+ return rc;
+}
+
+static int
+fy_parse_document_load_node(struct fy_parser *fyp, struct fy_document *fyd,
+ struct fy_eventp *fyep, struct fy_node **fynp,
+ int *depthp)
+{
+ struct fy_event *fye;
+ enum fy_event_type type;
+ int ret;
+
+ *fynp = NULL;
+
+ fyp_error_check(fyp, fyep || !fyp->stream_error, err_out,
+ "no event to process");
+
+ FYP_PARSE_ERROR_CHECK(fyp, 0, 0, FYEM_DOC,
+ fyep, err_out,
+ "premature end of event stream");
+
+ fyp_doc_debug(fyp, "in %s [%s]", __func__, fy_event_type_txt[fyep->e.type]);
+
+ fye = &fyep->e;
+
+ type = fye->type;
+
+ FYP_TOKEN_ERROR_CHECK(fyp, fy_event_get_token(fye), FYEM_DOC,
+ type == FYET_ALIAS || type == FYET_SCALAR ||
+ type == FYET_SEQUENCE_START || type == FYET_MAPPING_START, err_out,
+ "bad event");
+
+ (*depthp)++;
+
+ FYP_TOKEN_ERROR_CHECK(fyp, fy_event_get_token(fye), FYEM_DOC,
+ ((fyp->cfg.flags & FYPCF_DISABLE_DEPTH_LIMIT) ||
+ *depthp <= fy_depth_limit()), err_out,
+ "depth limit exceeded");
+
+ switch (type) {
+
+ case FYET_ALIAS:
+ case FYET_SCALAR:
+ ret = fy_parse_document_load_scalar(fyp, fyd,
+ fyep, fynp, depthp);
+ break;
+
+ case FYET_SEQUENCE_START:
+ ret = fy_parse_document_load_sequence(fyp, fyd,
+ fyep, fynp, depthp);
+ break;
+
+ case FYET_MAPPING_START:
+ ret = fy_parse_document_load_mapping(fyp, fyd,
+ fyep, fynp, depthp);
+ break;
+
+ default:
+ ret = 0;
+ break;
+ }
+
+ --(*depthp);
+ return ret;
+
+err_out:
+ fy_parse_eventp_recycle(fyp, fyep);
+ return -1;
+}
+
+int fy_parse_document_load_end(struct fy_parser *fyp, struct fy_document *fyd, struct fy_eventp *fyep)
+{
+ struct fy_event *fye;
+ int rc;
+
+ fyp_error_check(fyp, fyep || !fyp->stream_error, err_out,
+ "no event to process");
+
+ FYP_PARSE_ERROR_CHECK(fyp, 0, 0, FYEM_DOC,
+ fyep, err_out,
+ "premature end of event stream");
+
+ fyp_doc_debug(fyp, "in %s [%s]", __func__, fy_event_type_txt[fyep->e.type]);
+
+ fye = &fyep->e;
+
+ FYP_TOKEN_ERROR_CHECK(fyp, fy_event_get_token(fye), FYEM_DOC,
+ fye->type == FYET_DOCUMENT_END, err_out,
+ "bad event");
+
+ /* recycle the document end event */
+ fy_parse_eventp_recycle(fyp, fyep);
+
+ return 0;
+err_out:
+ rc = -1;
+ fy_parse_eventp_recycle(fyp, fyep);
+ return rc;
+}
+
+struct fy_document *fy_parse_load_document_recursive(struct fy_parser *fyp)
+{
+ struct fy_document *fyd = NULL;
+ struct fy_eventp *fyep = NULL;
+ struct fy_event *fye = NULL;
+ int rc, depth;
+ bool was_stream_start;
+
+again:
+ was_stream_start = false;
+ do {
+ /* get next event */
+ fyep = fy_parse_private(fyp);
+
+ /* no more */
+ if (!fyep)
+ return NULL;
+
+ was_stream_start = fyep->e.type == FYET_STREAM_START;
+
+ if (was_stream_start) {
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyep = NULL;
+ }
+
+ } while (was_stream_start);
+
+ fye = &fyep->e;
+
+ /* STREAM_END */
+ if (fye->type == FYET_STREAM_END) {
+ fy_parse_eventp_recycle(fyp, fyep);
+
+ /* final STREAM_END? */
+ if (fyp->state == FYPS_END)
+ return NULL;
+
+ /* multi-stream */
+ goto again;
+ }
+
+ FYP_TOKEN_ERROR_CHECK(fyp, fy_event_get_token(fye), FYEM_DOC,
+ fye->type == FYET_DOCUMENT_START, err_out,
+ "bad event");
+
+ fyd = fy_parse_document_create(fyp, fyep);
+ fyep = NULL;
+
+ fyp_error_check(fyp, fyd, err_out,
+ "fy_parse_document_create() failed");
+
+ fyp_doc_debug(fyp, "calling load_node() for root");
+ depth = 0;
+ rc = fy_parse_document_load_node(fyp, fyd, fy_parse_private(fyp),
+ &fyd->root, &depth);
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_parse_document_load_node() failed");
+
+ rc = fy_parse_document_load_end(fyp, fyd, fy_parse_private(fyp));
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_parse_document_load_node() failed");
+
+ /* always resolve parents */
+ fy_resolve_parent_node(fyd, fyd->root, NULL);
+
+ if (fyp->cfg.flags & FYPCF_RESOLVE_DOCUMENT) {
+ rc = fy_document_resolve(fyd);
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_document_resolve() failed");
+ }
+
+ return fyd;
+
+err_out:
+ fy_parse_eventp_recycle(fyp, fyep);
+ fy_parse_document_destroy(fyp, fyd);
+ return NULL;
+}
+
+struct fy_document *fy_parse_load_document_with_builder(struct fy_parser *fyp)
+{
+ struct fy_document_builder_cfg cfg;
+ struct fy_document *fyd;
+ int rc;
+
+ if (!fyp)
+ return NULL;
+
+ if (!fyp->fydb) {
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.parse_cfg = fyp->cfg;
+ cfg.userdata = fyp;
+ cfg.diag = fy_diag_ref(fyp->diag);
+
+ fyp->fydb = fy_document_builder_create(&cfg);
+ if (!fyp->fydb)
+ return NULL;
+ }
+
+ fyd = fy_document_builder_load_document(fyp->fydb, fyp);
+ if (!fyd)
+ return NULL;
+
+ if (fyp->cfg.flags & FYPCF_RESOLVE_DOCUMENT) {
+ rc = fy_document_resolve(fyd);
+ if (rc) {
+ fy_document_destroy(fyd);
+ fyp->stream_error = true;
+ return NULL;
+ }
+ }
+
+ return fyd;
+}
+
+struct fy_document *fy_parse_load_document(struct fy_parser *fyp)
+{
+ if (!fyp)
+ return NULL;
+
+ return !(fyp->cfg.flags & FYPCF_PREFER_RECURSIVE) ?
+ fy_parse_load_document_with_builder(fyp) :
+ fy_parse_load_document_recursive(fyp);
+}
+
+struct fy_node *fy_node_copy_internal(struct fy_document *fyd, struct fy_node *fyn_from,
+ struct fy_node *fyn_parent)
+{
+ struct fy_document *fyd_from;
+ struct fy_node *fyn, *fyni, *fynit;
+ struct fy_node_pair *fynp, *fynpt;
+ struct fy_anchor *fya, *fya_from;
+ const char *anchor;
+ size_t anchor_len;
+ int rc;
+
+ if (!fyd || !fyn_from || !fyn_from->fyd)
+ return NULL;
+
+ fyd_from = fyn_from->fyd;
+
+ fyn = fy_node_alloc(fyd, fyn_from->type);
+ fyd_error_check(fyd, fyn, err_out,
+ "fy_node_alloc() failed");
+
+ fyn->tag = fy_token_ref(fyn_from->tag);
+ fyn->style = fyn_from->style;
+ fyn->parent = fyn_parent;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ fyn->scalar = fy_token_ref(fyn_from->scalar);
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn_from->sequence); fyni;
+ fyni = fy_node_next(&fyn_from->sequence, fyni)) {
+
+ fynit = fy_node_copy_internal(fyd, fyni, fyn);
+ fyd_error_check(fyd, fynit, err_out,
+ "fy_node_copy_internal() failed");
+
+ fy_node_list_add_tail(&fyn->sequence, fynit);
+ fynit->attached = true;
+ }
+
+ break;
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn_from->mapping); fynp;
+ fynp = fy_node_pair_next(&fyn_from->mapping, fynp)) {
+
+ fynpt = fy_node_pair_alloc(fyd);
+ fyd_error_check(fyd, fynpt, err_out,
+ "fy_node_pair_alloc() failed");
+
+ fynpt->key = fy_node_copy_internal(fyd, fynp->key, fyn);
+ fynpt->value = fy_node_copy_internal(fyd, fynp->value, fyn);
+ fynp->parent = fyn;
+
+ fy_node_pair_list_add_tail(&fyn->mapping, fynpt);
+ if (fyn->xl) {
+ rc = fy_accel_insert(fyn->xl, fynpt->key, fynpt);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_accel_insert() failed");
+ }
+ if (fynpt->key) {
+ fynpt->key->attached = true;
+ fynpt->key->key_root = true;
+ }
+ if (fynpt->value)
+ fynpt->value->attached = true;
+ }
+ break;
+ }
+
+ /* drop an anchor to the copy */
+ for (fya_from = fy_anchor_list_head(&fyd_from->anchors); fya_from;
+ fya_from = fy_anchor_next(&fyd_from->anchors, fya_from)) {
+ if (fyn_from == fya_from->fyn)
+ break;
+ }
+
+ /* source node has an anchor */
+ if (fya_from) {
+ fya = fy_document_lookup_anchor_by_token(fyd, fya_from->anchor);
+ if (!fya) {
+ fyd_doc_debug(fyd, "new anchor");
+ /* update the new anchor position */
+ rc = fy_document_register_anchor(fyd, fyn, fya_from->anchor);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_document_register_anchor() failed");
+
+ fy_token_ref(fya_from->anchor);
+ } else {
+ anchor = fy_anchor_get_text(fya, &anchor_len);
+ fyd_error_check(fyd, anchor, err_out,
+ "fy_anchor_get_text() failed");
+ fyd_doc_debug(fyd, "not overwritting anchor %.*s", (int)anchor_len, anchor);
+ }
+ }
+
+ return fyn;
+
+err_out:
+ return NULL;
+}
+
+struct fy_node *fy_node_copy(struct fy_document *fyd, struct fy_node *fyn_from)
+{
+ struct fy_node *fyn;
+
+ if (!fyd)
+ return NULL;
+
+ fyn = fy_node_copy_internal(fyd, fyn_from, NULL);
+ if (!fyn) {
+ fyd->diag->on_error = false;
+ return NULL;
+ }
+
+ return fyn;
+}
+
+struct fy_document *fy_document_clone(struct fy_document *fydsrc)
+{
+ struct fy_document *fyd = NULL;
+
+ if (!fydsrc)
+ return NULL;
+
+ fyd = fy_document_create(&fydsrc->parse_cfg);
+ if (!fyd)
+ return NULL;
+
+ /* drop the default document state */
+ fy_document_state_unref(fyd->fyds);
+ /* and use the source document state (and ref it) */
+ fyd->fyds = fy_document_state_ref(fydsrc->fyds);
+ assert(fyd->fyds);
+
+ if (fydsrc->root) {
+ fyd->root = fy_node_copy(fyd, fydsrc->root);
+ if (!fyd->root)
+ goto err_out;
+ }
+
+ return fyd;
+err_out:
+ fy_document_destroy(fyd);
+ return NULL;
+}
+
+int fy_node_copy_to_scalar(struct fy_document *fyd, struct fy_node *fyn_to, struct fy_node *fyn_from)
+{
+ struct fy_node *fyn, *fyni;
+ struct fy_node_pair *fynp;
+
+ fyn = fy_node_copy(fyd, fyn_from);
+ if (!fyn)
+ return -1;
+
+ /* the node is guaranteed to be a scalar */
+ fy_token_unref(fyn_to->tag);
+ fyn_to->tag = NULL;
+ fy_token_unref(fyn_to->scalar);
+ fyn_to->scalar = NULL;
+
+ fyn_to->type = fyn->type;
+ fyn_to->tag = fy_token_ref(fyn->tag);
+ fyn_to->style = fyn->style;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ fyn_to->scalar = fyn->scalar;
+ fyn->scalar = NULL;
+ break;
+ case FYNT_SEQUENCE:
+ fy_node_list_init(&fyn_to->sequence);
+ while ((fyni = fy_node_list_pop(&fyn->sequence)) != NULL)
+ fy_node_list_add_tail(&fyn_to->sequence, fyni);
+ break;
+ case FYNT_MAPPING:
+ fy_node_pair_list_init(&fyn_to->mapping);
+ while ((fynp = fy_node_pair_list_pop(&fyn->mapping)) != NULL) {
+ if (fyn->xl)
+ fy_accel_remove(fyn->xl, fynp->key);
+ fy_node_pair_list_add_tail(&fyn_to->mapping, fynp);
+ if (fyn_to->xl)
+ fy_accel_insert(fyn_to->xl, fynp->key, fynp);
+ }
+ break;
+ }
+
+ /* and free */
+ fy_node_free(fyn);
+
+ return 0;
+}
+
+static int fy_document_node_update_tags(struct fy_document *fyd, struct fy_node *fyn)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi;
+ struct fy_token *fyt_td;
+ const char *handle;
+ size_t handle_size;
+ int rc;
+
+ if (!fyd || !fyn)
+ return 0;
+
+ /* replace tag reference with the one that the document contains */
+ if (fyn->tag) {
+ fyd_error_check(fyd, fyn->tag->type == FYTT_TAG, err_out,
+ "bad node tag");
+
+ handle = fy_tag_directive_token_handle(fyn->tag->tag.fyt_td, &handle_size);
+ fyd_error_check(fyd, handle, err_out,
+ "bad tag directive token");
+
+ fyt_td = fy_document_state_lookup_tag_directive(fyd->fyds, handle, handle_size);
+ fyd_error_check(fyd, fyt_td, err_out,
+ "Missing tag directive with handle=%.*s", (int)handle_size, handle);
+
+ /* need to replace this */
+ if (fyt_td != fyn->tag->tag.fyt_td) {
+ fy_token_unref(fyn->tag->tag.fyt_td);
+ fyn->tag->tag.fyt_td = fy_token_ref(fyt_td);
+
+ }
+ }
+
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ rc = fy_document_node_update_tags(fyd, fyni);
+ if (rc)
+ goto err_out_rc;
+ }
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ /* the parent of the key is always NULL */
+ rc = fy_document_node_update_tags(fyd, fynp->key);
+ if (rc)
+ goto err_out_rc;
+
+ rc = fy_document_node_update_tags(fyd, fynp->value);
+ if (rc)
+ goto err_out_rc;
+ }
+ break;
+ }
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ return rc;
+}
+
+int fy_node_insert(struct fy_node *fyn_to, struct fy_node *fyn_from)
+{
+ struct fy_document *fyd;
+ struct fy_node *fyn_parent, *fyn_cpy, *fyni, *fyn_prev;
+ struct fy_node_pair *fynp, *fynpi, *fynpj;
+ int rc;
+
+ if (!fyn_to || !fyn_to->fyd)
+ return -1;
+
+ fyd = fyn_to->fyd;
+ assert(fyd);
+
+ fyn_parent = fy_node_get_document_parent(fyn_to);
+ fynp = NULL;
+ if (fyn_parent) {
+ fyd_error_check(fyd, fyn_parent->type != FYNT_SCALAR, err_out,
+ "Illegal scalar parent node type");
+
+ fyd_error_check(fyd, fyn_from, err_out,
+ "Illegal NULL source node");
+
+ if (fyn_parent->type == FYNT_MAPPING) {
+ /* find mapping pair that contains the `to` node */
+ for (fynp = fy_node_pair_list_head(&fyn_parent->mapping); fynp;
+ fynp = fy_node_pair_next(&fyn_parent->mapping, fynp)) {
+ if (fynp->value == fyn_to)
+ break;
+ }
+ }
+ }
+
+ /* verify no funkiness on root */
+ assert(fyn_parent || fyn_to == fyd->root);
+
+ /* deleting target */
+ if (!fyn_from) {
+ fyn_to->parent = NULL;
+
+ if (!fyn_parent) {
+ fyd_doc_debug(fyd, "Deleting root node");
+ fy_node_detach_and_free(fyn_to);
+ fyd->root = NULL;
+ } else if (fyn_parent->type == FYNT_SEQUENCE) {
+ fyd_doc_debug(fyd, "Deleting sequence node");
+ fy_node_list_del(&fyn_parent->sequence, fyn_to);
+ fy_node_detach_and_free(fyn_to);
+ } else {
+ fyd_doc_debug(fyd, "Deleting mapping node");
+ /* should never happen, it's checked right above, but play safe */
+ assert(fyn_parent->type == FYNT_MAPPING);
+
+ fyd_error_check(fyd, fynp, err_out,
+ "Illegal mapping node found");
+
+ fy_node_pair_list_del(&fyn_parent->mapping, fynp);
+ if (fyn_parent->xl)
+ fy_accel_remove(fyn_parent->xl, fynp->key);
+ /* this will also delete fyn_to */
+ fy_node_pair_detach_and_free(fynp);
+ }
+ return 0;
+ }
+
+ /*
+ * from: scalar
+ *
+ * to: another-scalar -> scalar
+ * to: { key: value } -> scalar
+ * to: [ seq0, seq1 ] -> scalar
+ *
+ * from: [ seq2 ]
+ * to: scalar -> [ seq2 ]
+ * to: { key: value } -> [ seq2 ]
+ * to: [ seq0, seq1 ] -> [ seq0, seq1, sec2 ]
+ *
+ * from: { another-key: another-value }
+ * to: scalar -> { another-key: another-value }
+ * to: { key: value } -> { key: value, another-key: another-value }
+ * to: [ seq0, seq1 ] -> { another-key: another-value }
+ *
+ * from: { key: another-value }
+ * to: scalar -> { key: another-value }
+ * to: { key: value } -> { key: another-value }
+ * to: [ seq0, seq1 ] -> { key: another-value }
+ *
+ */
+
+ /* if types of `from` and `to` differ (or it's a scalar), it's a replace */
+ if (fyn_from->type != fyn_to->type || fyn_from->type == FYNT_SCALAR) {
+
+ fyn_cpy = fy_node_copy(fyd, fyn_from);
+ fyd_error_check(fyd, fyn_cpy, err_out,
+ "fy_node_copy() failed");
+
+ if (!fyn_parent) {
+ fyd_doc_debug(fyd, "Replacing root node");
+ fy_node_detach_and_free(fyd->root);
+ fyd->root = fyn_cpy;
+ } else if (fyn_parent->type == FYNT_SEQUENCE) {
+ fyd_doc_debug(fyd, "Replacing sequence node");
+
+ /* get previous */
+ fyn_prev = fy_node_prev(&fyn_parent->sequence, fyn_to);
+
+ /* delete */
+ fy_node_list_del(&fyn_parent->sequence, fyn_to);
+ fy_node_detach_and_free(fyn_to);
+
+ /* if there's no previous insert to head */
+ if (!fyn_prev)
+ fy_node_list_add(&fyn_parent->sequence, fyn_cpy);
+ else
+ fy_node_list_insert_after(&fyn_parent->sequence, fyn_prev, fyn_cpy);
+ } else {
+ fyd_doc_debug(fyd, "Replacing mapping node value");
+ /* should never happen, it's checked right above, but play safe */
+ assert(fyn_parent->type == FYNT_MAPPING);
+ fyd_error_check(fyd, fynp, err_out,
+ "Illegal mapping node found");
+
+ fy_node_detach_and_free(fynp->value);
+ fynp->value = fyn_cpy;
+ }
+
+ return 0;
+ }
+
+ /* types match, if it's a sequence append */
+ if (fyn_to->type == FYNT_SEQUENCE) {
+
+ fyd_doc_debug(fyd, "Appending to sequence node");
+
+ for (fyni = fy_node_list_head(&fyn_from->sequence); fyni;
+ fyni = fy_node_next(&fyn_from->sequence, fyni)) {
+
+ fyn_cpy = fy_node_copy(fyd, fyni);
+ fyd_error_check(fyd, fyn_cpy, err_out,
+ "fy_node_copy() failed");
+
+ fy_node_list_add_tail(&fyn_to->sequence, fyn_cpy);
+ fyn_cpy->attached = true;
+ }
+ } else {
+ /* only mapping is possible here */
+
+ /* iterate over all the keys in the `from` */
+ for (fynpi = fy_node_pair_list_head(&fyn_from->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fyn_from->mapping, fynpi)) {
+
+ if (fyn_to->xl) {
+ fynpj = fy_node_accel_lookup_by_node(fyn_to, fynpi->key);
+ } else {
+ /* find whether the key already exists */
+ for (fynpj = fy_node_pair_list_head(&fyn_to->mapping); fynpj;
+ fynpj = fy_node_pair_next(&fyn_to->mapping, fynpj)) {
+
+ if (fy_node_compare(fynpi->key, fynpj->key))
+ break;
+ }
+ }
+
+ if (!fynpj) {
+ fyd_doc_debug(fyd, "Appending to mapping node");
+
+ /* not found? append it */
+ fynpj = fy_node_pair_alloc(fyd);
+ fyd_error_check(fyd, fynpj, err_out,
+ "fy_node_pair_alloc() failed");
+
+ fynpj->key = fy_node_copy(fyd, fynpi->key);
+ fyd_error_check(fyd, !fynpi->key || fynpj->key, err_out,
+ "fy_node_copy() failed");
+ fynpj->value = fy_node_copy(fyd, fynpi->value);
+ fyd_error_check(fyd, !fynpi->value || fynpj->value, err_out,
+ "fy_node_copy() failed");
+
+ fy_node_pair_list_add_tail(&fyn_to->mapping, fynpj);
+ if (fyn_to->xl)
+ fy_accel_insert(fyn_to->xl, fynpj->key, fynpj);
+
+ if (fynpj->key)
+ fynpj->key->attached = true;
+ if (fynpj->value)
+ fynpj->value->attached = true;
+
+ } else {
+ fyd_doc_debug(fyd, "Updating mapping node value (deep merge)");
+
+ rc = fy_node_insert(fynpj->value, fynpi->value);
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_node_insert() failed");
+ }
+ }
+ }
+
+ /* adjust parents */
+ switch (fyn_to->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn_to->sequence); fyni;
+ fyni = fy_node_next(&fyn_to->sequence, fyni)) {
+
+ fyni->parent = fyn_to;
+ }
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn_to->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn_to->mapping, fynp);
+
+ if (fynp->key) {
+ fynp->key->parent = fyn_to;
+ fynp->key->key_root = true;
+ }
+ if (fynp->value)
+ fynp->value->parent = fyn_to;
+ fynp->parent = fyn_to;
+ }
+ break;
+ }
+
+ /* if the documents differ, merge their states */
+ if (fyn_to->fyd != fyn_from->fyd) {
+ rc = fy_document_state_merge(fyn_to->fyd->fyds, fyn_from->fyd->fyds);
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_document_state_merge() failed");
+
+ rc = fy_document_node_update_tags(fyd, fy_document_root(fyd));
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_document_node_update_tags() failed");
+ }
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ return rc;
+}
+
+int fy_document_insert_at(struct fy_document *fyd,
+ const char *path, size_t pathlen,
+ struct fy_node *fyn)
+{
+ int rc;
+ struct fy_node *fyn2;
+
+ fyn2 = fy_node_by_path(fy_document_root(fyd), path, pathlen, FYNWF_DONT_FOLLOW);
+ rc = fy_node_insert(fyn2, fyn);
+
+ fy_node_free(fyn);
+
+ return rc;
+}
+
+struct fy_token *fy_document_tag_directive_iterate(struct fy_document *fyd, void **prevp)
+{
+ struct fy_token_list *fytl;
+
+ if (!fyd || !fyd->fyds || !prevp)
+ return NULL;
+
+ fytl = &fyd->fyds->fyt_td;
+
+ return *prevp = *prevp ? fy_token_next(fytl, *prevp) : fy_token_list_head(fytl);
+}
+
+struct fy_token *fy_document_tag_directive_lookup(struct fy_document *fyd, const char *handle)
+{
+ struct fy_token *fyt;
+ void *iter;
+ const char *h;
+ size_t h_size, len;
+
+ if (!fyd || !handle)
+ return NULL;
+ len = strlen(handle);
+
+ iter = NULL;
+ while ((fyt = fy_document_tag_directive_iterate(fyd, &iter)) != NULL) {
+ h = fy_tag_directive_token_handle(fyt, &h_size);
+ if (!h)
+ continue;
+ if (h_size == len && !memcmp(h, handle, len))
+ return fyt;
+ }
+ return NULL;
+}
+
+int fy_document_tag_directive_add(struct fy_document *fyd, const char *handle, const char *prefix)
+{
+ struct fy_token *fyt;
+
+ if (!fyd || !fyd->fyds || !handle || !prefix)
+ return -1;
+
+ /* it must not exist */
+ fyt = fy_document_tag_directive_lookup(fyd, handle);
+ if (fyt)
+ return -1;
+
+ return fy_document_state_append_tag(fyd->fyds, handle, prefix, false);
+}
+
+int fy_document_tag_directive_remove(struct fy_document *fyd, const char *handle)
+{
+ struct fy_token *fyt;
+
+ if (!fyd || !fyd->fyds || !handle)
+ return -1;
+
+ /* it must not exist */
+ fyt = fy_document_tag_directive_lookup(fyd, handle);
+ if (!fyt || fyt->refs != 1)
+ return -1;
+
+ fy_token_list_del(&fyd->fyds->fyt_td, fyt);
+ fy_token_unref(fyt);
+
+ return 0;
+}
+
+static int fy_resolve_alias(struct fy_document *fyd, struct fy_node *fyn)
+{
+ struct fy_node *fyn_copy = NULL;
+ int rc;
+
+ fyn_copy = fy_node_resolve_alias(fyn);
+ FYD_NODE_ERROR_CHECK(fyd, fyn, FYEM_DOC,
+ fyn_copy, err_out,
+ "invalid alias");
+
+ rc = fy_node_copy_to_scalar(fyd, fyn, fyn_copy);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_node_copy_to_scalar() failed");
+
+ return 0;
+
+err_out:
+ fyd->diag->on_error = false;
+ return -1;
+}
+
+static struct fy_node *
+fy_node_follow_alias(struct fy_node *fyn, enum fy_node_walk_flags flags)
+{
+ enum fy_node_walk_flags ptr_flags;
+ struct fy_anchor *fya;
+ const char *anchor_text, *s, *e, *p, *path;
+ size_t anchor_len, path_len;
+ struct fy_node *fyn_path_root;
+ unsigned int marker;
+
+ if (!fyn || !fy_node_is_alias(fyn))
+ return NULL;
+
+ ptr_flags = flags & FYNWF_PTR(FYNWF_PTR_MASK);
+ if (ptr_flags == FYNWF_PTR_YPATH)
+ return fy_node_alias_resolve_by_ypath(fyn);
+
+ /* try regular label target */
+ fya = fy_document_lookup_anchor_by_token(fyn->fyd, fyn->scalar);
+ if (fya)
+ return fya->fyn;
+
+ anchor_text = fy_token_get_text(fyn->scalar, &anchor_len);
+ if (!anchor_text)
+ return NULL;
+
+ s = anchor_text;
+ e = s + anchor_len;
+
+ fyn_path_root = NULL;
+
+ if (ptr_flags == FYNWF_PTR_YAML && (p = memchr(s, '/', e - s)) != NULL) {
+ /* fyd_notice(fyn->fyd, "%s: alias contains a path component %.*s",
+ __func__, (int)(e - p - 1), p + 1); */
+
+ if (p > s) {
+
+ fya = fy_document_lookup_anchor(fyn->fyd, s, p - s);
+ if (!fya) {
+ /* fyd_notice(fyn->fyd, "%s: unable to resolve alias %.*s @%s",
+ __func__, (int)(p - s), s, fy_node_get_path(fya->fyn)); */
+ return NULL;
+ }
+
+ /* fyd_notice(fyn->fyd, "%s: alias base %.*s @%s",
+ __func__, (int)(p - s), s, fy_node_get_path(fya->fyn)); */
+ path = ++p;
+ path_len = e - p;
+
+ fyn_path_root = fya->fyn;
+
+ } else {
+ /* fyd_notice(fyn->fyd, "%s: absolute %.*s @%s",
+ __func__, (int)(p - s), s, fy_node_get_path(fya->fyn)); */
+ path = s;
+ path_len = e - s;
+
+ fyn_path_root = fyn->fyd->root;
+ }
+ }
+
+ if (!fyn_path_root)
+ return NULL;
+
+ marker = fy_node_walk_marker_from_flags(flags);
+ if (marker >= FYNWF_MAX_USER_MARKER)
+ return NULL;
+
+ /* use the next marker */
+ flags &= ~FYNWF_MARKER(FYNWF_MARKER_MASK);
+ flags |= FYNWF_MARKER(marker + 1);
+
+ return fy_node_by_path_internal(fyn_path_root, path, path_len, flags);
+}
+
+static bool fy_node_pair_is_merge_key(struct fy_node_pair *fynp)
+{
+ struct fy_node *fyn = fynp->key;
+
+ return fyn && fyn->type == FYNT_SCALAR && fyn->style == FYNS_PLAIN &&
+ fy_plain_atom_streq(fy_token_atom(fyn->scalar), "<<");
+}
+
+static struct fy_node *fy_alias_get_merge_mapping(struct fy_document *fyd, struct fy_node *fyn)
+{
+ struct fy_anchor *fya;
+
+ /* must be an alias */
+ if (!fy_node_is_alias(fyn))
+ return NULL;
+
+ /* anchor must exist */
+ fya = fy_document_lookup_anchor_by_token(fyd, fyn->scalar);
+ if (!fya)
+ return NULL;
+
+ /* and it must be a mapping */
+ if (fya->fyn->type != FYNT_MAPPING)
+ return NULL;
+
+ return fya->fyn;
+}
+
+static bool fy_node_pair_is_valid_merge_key(struct fy_document *fyd, struct fy_node_pair *fynp)
+{
+ struct fy_node *fyn, *fyni, *fynm;
+
+ fyn = fynp->value;
+
+ /* value must exist */
+ if (!fyn)
+ return false;
+
+ /* scalar alias */
+ fynm = fy_alias_get_merge_mapping(fyd, fyn);
+ if (fynm)
+ return true;
+
+ /* it must be a sequence then */
+ if (fyn->type != FYNT_SEQUENCE)
+ return false;
+
+ /* the sequence must only contain valid aliases for mapping */
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ /* sequence of aliases only! */
+ fynm = fy_alias_get_merge_mapping(fyd, fyni);
+ if (!fynm)
+ return false;
+
+ }
+
+ return true;
+}
+
+static int fy_resolve_merge_key_populate(struct fy_document *fyd, struct fy_node *fyn,
+ struct fy_node_pair *fynp, struct fy_node *fynm)
+{
+ struct fy_node_pair *fynpi, *fynpn;
+
+ if (!fyd)
+ return -1;
+
+ fyd_error_check(fyd,
+ fyn && fynp && fynm && fyn->type == FYNT_MAPPING && fynm->type == FYNT_MAPPING,
+ err_out, "bad inputs to %s", __func__);
+
+ for (fynpi = fy_node_pair_list_head(&fynm->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fynm->mapping, fynpi)) {
+
+ /* if we don't allow duplicate keys */
+ if (!(fyd->parse_cfg.flags & FYPCF_ALLOW_DUPLICATE_KEYS)) {
+
+ /* make sure we don't override an already existing key */
+ if (fy_node_mapping_key_is_duplicate(fyn, fynpi->key))
+ continue;
+ }
+
+ fynpn = fy_node_pair_alloc(fyd);
+ fyd_error_check(fyd, fynpn, err_out,
+ "fy_node_pair_alloc() failed");
+
+ fynpn->key = fy_node_copy(fyd, fynpi->key);
+ fynpn->value = fy_node_copy(fyd, fynpi->value);
+
+ fy_node_pair_list_insert_after(&fyn->mapping, fynp, fynpn);
+ if (fyn->xl)
+ fy_accel_insert(fyn->xl, fynpn->key, fynpn);
+ }
+
+ return 0;
+
+err_out:
+ return -1;
+}
+
+static int fy_resolve_merge_key(struct fy_document *fyd, struct fy_node *fyn, struct fy_node_pair *fynp)
+{
+ struct fy_node *fynv, *fyni, *fynm;
+ int rc;
+
+ /* it must be a valid merge key value */
+ FYD_NODE_ERROR_CHECK(fyd, fynp->value, FYEM_DOC,
+ fy_node_pair_is_valid_merge_key(fyd, fynp), err_out,
+ "invalid merge key value");
+
+ fynv = fynp->value;
+ fynm = fy_alias_get_merge_mapping(fyd, fynv);
+ if (fynm) {
+ rc = fy_resolve_merge_key_populate(fyd, fyn, fynp, fynm);
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_resolve_merge_key_populate() failed");
+
+ return 0;
+ }
+
+ /* it must be a sequence then */
+ fyd_error_check(fyd, fynv->type == FYNT_SEQUENCE, err_out,
+ "invalid node type to use for merge key");
+
+ /* the sequence must only contain valid aliases for mapping */
+ for (fyni = fy_node_list_head(&fynv->sequence); fyni;
+ fyni = fy_node_next(&fynv->sequence, fyni)) {
+
+ fynm = fy_alias_get_merge_mapping(fyd, fyni);
+ fyd_error_check(fyd, fynm, err_out,
+ "invalid merge key sequence item (not an alias)");
+
+ rc = fy_resolve_merge_key_populate(fyd, fyn, fynp, fynm);
+ fyd_error_check(fyd, !rc, err_out_rc,
+ "fy_resolve_merge_key_populate() failed");
+ }
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ return rc;
+}
+
+/* the anchors are scalars that have the FYNS_ALIAS style */
+static int fy_resolve_anchor_node(struct fy_document *fyd, struct fy_node *fyn)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi, *fynpit;
+ int rc, ret_rc = 0;
+ struct fy_token *fyt;
+
+ if (!fyn)
+ return 0;
+
+ if (fy_node_is_alias(fyn))
+ return fy_resolve_alias(fyd, fyn);
+
+ if (fyn->type == FYNT_SEQUENCE) {
+
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ rc = fy_resolve_anchor_node(fyd, fyni);
+ if (rc && !ret_rc)
+ ret_rc = rc;
+ }
+
+ } else if (fyn->type == FYNT_MAPPING) {
+
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ if (fy_node_pair_is_merge_key(fynp)) {
+ rc = fy_resolve_merge_key(fyd, fyn, fynp);
+ if (rc && !ret_rc)
+ ret_rc = rc;
+
+ /* remove this node pair */
+ if (!rc) {
+ fy_node_pair_list_del(&fyn->mapping, fynp);
+ if (fyn->xl)
+ fy_accel_remove(fyn->xl, fynp->key);
+ fy_node_pair_detach_and_free(fynp);
+ }
+
+ } else {
+
+ rc = fy_resolve_anchor_node(fyd, fynp->key);
+
+ if (!rc) {
+
+ /* check whether the keys are duplicate */
+ for (fynpit = fy_node_pair_list_head(&fyn->mapping); fynpit;
+ fynpit = fy_node_pair_next(&fyn->mapping, fynpit)) {
+
+ /* skip this node pair */
+ if (fynpit == fynp)
+ continue;
+
+ if (!fy_node_compare(fynpit->key, fynp->key))
+ continue;
+
+ /* whoops, duplicate key after resolution */
+ fyt = NULL;
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ fyt = fyn->scalar;
+ break;
+ case FYNT_SEQUENCE:
+ fyt = fyn->sequence_start;
+ break;
+ case FYNT_MAPPING:
+ fyt = fyn->mapping_start;
+ break;
+ }
+
+ FYD_TOKEN_ERROR_CHECK(fyd, fyt, FYEM_DOC,
+ false, err_out,
+ "duplicate key after resolving");
+
+ }
+
+ }
+
+ if (rc && !ret_rc)
+ ret_rc = rc;
+
+ rc = fy_resolve_anchor_node(fyd, fynp->value);
+ if (rc && !ret_rc)
+ ret_rc = rc;
+
+ }
+ }
+ }
+
+ return ret_rc;
+
+err_out:
+ return -1;
+}
+
+static void fy_resolve_parent_node(struct fy_document *fyd, struct fy_node *fyn, struct fy_node *fyn_parent)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi;
+
+ if (!fyn)
+ return;
+
+ fyn->parent = fyn_parent;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ fy_resolve_parent_node(fyd, fyni, fyn);
+ }
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ fy_resolve_parent_node(fyd, fynp->key, fyn);
+ fy_resolve_parent_node(fyd, fynp->value, fyn);
+ fynp->parent = fyn;
+ }
+ break;
+ }
+}
+
+typedef void (*fy_node_applyf)(struct fy_node *fyn);
+
+void fy_node_apply(struct fy_node *fyn, fy_node_applyf func)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp;
+
+ if (!fyn || !func)
+ return;
+
+ (*func)(fyn);
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni))
+ fy_node_apply(fyni, func);
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp;
+ fynp = fy_node_pair_next(&fyn->mapping, fynp)) {
+
+ fy_node_apply(fynp->key, func);
+ fy_node_apply(fynp->value, func);
+ }
+ break;
+ }
+}
+
+static void clear_system_marks(struct fy_node *fyn)
+{
+ fyn->marks &= ~FYNWF_SYSTEM_MARKS;
+}
+
+/* clear all the system markers */
+void fy_node_clear_system_marks(struct fy_node *fyn)
+{
+ fy_node_apply(fyn, clear_system_marks);
+}
+
+int fy_document_resolve(struct fy_document *fyd)
+{
+ int rc;
+ bool ret;
+
+ if (!fyd)
+ return 0;
+
+ fy_node_clear_system_marks(fyd->root);
+
+ /* for resolution to work, no reference loops should exist */
+ ret = fy_check_ref_loop(fyd, fyd->root,
+ FYNWF_MAXDEPTH_DEFAULT | FYNWF_FOLLOW, NULL);
+
+ fy_node_clear_system_marks(fyd->root);
+
+ if (ret)
+ goto err_out;
+
+
+ /* now resolve any anchor nodes */
+ rc = fy_resolve_anchor_node(fyd, fyd->root);
+ if (rc)
+ goto err_out_rc;
+
+ /* redo parent resolution */
+ fy_resolve_parent_node(fyd, fyd->root, NULL);
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ fyd->diag->on_error = false;
+ return rc;
+}
+
+void fy_document_free_nodes(struct fy_document *fyd)
+{
+ struct fy_document *fyd_child;
+
+ for (fyd_child = fy_document_list_first(&fyd->children); fyd_child;
+ fyd_child = fy_document_next(&fyd->children, fyd_child))
+ fy_document_free_nodes(fyd_child);
+
+ fy_node_detach_and_free(fyd->root);
+ fyd->root = NULL;
+}
+
+void fy_document_destroy(struct fy_document *fyd)
+{
+ struct fy_document *fyd_child;
+
+ /* both the document and the parser object must exist */
+ if (!fyd)
+ return;
+
+ /* we have to free the nodes first */
+ fy_document_free_nodes(fyd);
+
+ /* recursively delete children */
+ while ((fyd_child = fy_document_list_pop(&fyd->children)) != NULL) {
+ fyd_child->parent = NULL;
+ fy_document_destroy(fyd_child);
+ }
+
+ fy_parse_document_destroy(NULL, fyd);
+}
+
+int fy_document_set_parent(struct fy_document *fyd, struct fy_document *fyd_child)
+{
+
+ if (!fyd || !fyd_child || fyd_child->parent)
+ return -1;
+
+ fyd_child->parent = fyd;
+ fy_document_list_add_tail(&fyd->children, fyd_child);
+
+ return 0;
+}
+
+static const struct fy_parse_cfg doc_parse_default_cfg = {
+ .flags = FYPCF_DEFAULT_DOC,
+};
+
+struct fy_document *fy_document_create(const struct fy_parse_cfg *cfg)
+{
+ struct fy_document *fyd = NULL;
+ struct fy_diag *diag;
+ int rc;
+
+ if (!cfg)
+ cfg = &doc_parse_default_cfg;
+
+ fyd = malloc(sizeof(*fyd));
+ if (!fyd)
+ goto err_out;
+
+ memset(fyd, 0, sizeof(*fyd));
+ fyd->parse_cfg = *cfg;
+
+ diag = cfg->diag;
+ if (!diag) {
+ diag = fy_diag_create(NULL);
+ if (!diag)
+ goto err_out;
+ } else
+ fy_diag_ref(diag);
+
+ fyd->diag = diag;
+
+ fy_anchor_list_init(&fyd->anchors);
+ if (fy_document_is_accelerated(fyd)) {
+ fyd->axl = malloc(sizeof(*fyd->axl));
+ fyd_error_check(fyd, fyd->axl, err_out,
+ "malloc() failed");
+
+ /* start with a very small bucket list */
+ rc = fy_accel_setup(fyd->axl, &hd_anchor, fyd, 8);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_accel_setup() failed");
+
+ fyd->naxl = malloc(sizeof(*fyd->naxl));
+ fyd_error_check(fyd, fyd->axl, err_out,
+ "malloc() failed");
+
+ /* start with a very small bucket list */
+ rc = fy_accel_setup(fyd->naxl, &hd_nanchor, fyd, 8);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_accel_setup() failed");
+ }
+ fyd->root = NULL;
+
+ /* we don't do document create version setting,
+ * perhaps we should in the future
+ */
+ fyd->fyds = fy_document_state_default(NULL, NULL);
+ fyd_error_check(fyd, fyd->fyds, err_out,
+ "fy_document_state_default() failed");
+
+ /* turn on JSON mode if it's forced */
+ fyd->fyds->json_mode = (cfg->flags &
+ (FYPCF_JSON_MASK << FYPCF_JSON_SHIFT)) == FYPCF_JSON_FORCE;
+
+ fy_document_list_init(&fyd->children);
+
+ return fyd;
+
+err_out:
+ fy_parse_document_destroy(NULL, fyd);
+ return NULL;
+}
+
+struct fy_document_build_string_ctx {
+ const char *str;
+ size_t len;
+};
+
+static int parser_setup_from_string(struct fy_parser *fyp, void *user)
+{
+ struct fy_document_build_string_ctx *ctx = user;
+
+ return fy_parser_set_string(fyp, ctx->str, ctx->len);
+}
+
+struct fy_document_build_malloc_string_ctx {
+ char *str;
+ size_t len;
+};
+
+static int parser_setup_from_malloc_string(struct fy_parser *fyp, void *user)
+{
+ struct fy_document_build_malloc_string_ctx *ctx = user;
+
+ return fy_parser_set_malloc_string(fyp, ctx->str, ctx->len);
+}
+
+struct fy_document_build_file_ctx {
+ const char *file;
+};
+
+static int parser_setup_from_file(struct fy_parser *fyp, void *user)
+{
+ struct fy_document_build_file_ctx *ctx = user;
+
+ return fy_parser_set_input_file(fyp, ctx->file);
+}
+
+struct fy_document_build_fp_ctx {
+ const char *name;
+ FILE *fp;
+};
+
+static int parser_setup_from_fp(struct fy_parser *fyp, void *user)
+{
+ struct fy_document_build_fp_ctx *ctx = user;
+
+ return fy_parser_set_input_fp(fyp, ctx->name, ctx->fp);
+}
+
+struct fy_document_vbuildf_ctx {
+ const char *fmt;
+ va_list ap;
+};
+
+static int parser_setup_from_fmt_ap(struct fy_parser *fyp, void *user)
+{
+ struct fy_document_vbuildf_ctx *vctx = user;
+ va_list ap, ap_orig;
+ int size, sizew;
+ char *buf;
+
+ /* first try without allocating */
+ va_copy(ap_orig, vctx->ap);
+ size = vsnprintf(NULL, 0, vctx->fmt, ap_orig);
+ va_end(ap_orig);
+
+ fyp_error_check(fyp, size >= 0, err_out,
+ "vsnprintf() failed");
+
+ buf = malloc(size + 1);
+ fyp_error_check(fyp, buf, err_out,
+ "malloc() failed");
+
+ va_copy(ap, vctx->ap);
+ sizew = vsnprintf(buf, size + 1, vctx->fmt, ap);
+ fyp_error_check(fyp, sizew == size, err_out,
+ "vsnprintf() failed");
+ va_end(ap);
+
+ buf[size] = '\0';
+
+ return fy_parser_set_malloc_string(fyp, buf, size);
+
+err_out:
+ return -1;
+}
+
+static struct fy_document *fy_document_build_internal(const struct fy_parse_cfg *cfg,
+ int (*parser_setup)(struct fy_parser *fyp, void *user),
+ void *user)
+{
+ struct fy_parser fyp_data, *fyp = &fyp_data;
+ struct fy_document *fyd = NULL;
+ struct fy_eventp *fyep;
+ bool got_stream_end;
+ int rc;
+
+ if (!parser_setup)
+ return NULL;
+
+ if (!cfg)
+ cfg = &doc_parse_default_cfg;
+
+ rc = fy_parse_setup(fyp, cfg);
+ if (rc)
+ return NULL;
+
+ rc = (*parser_setup)(fyp, user);
+ fyp_error_check(fyp, !rc, err_out,
+ "parser_setup() failed");
+
+ fyd = fy_parse_load_document(fyp);
+
+ /* we're going to handle stream errors from now */
+ if (!fyd)
+ fyp->stream_error = false;
+
+ /* if we collect diagnostics, we can continue */
+ fyp_error_check(fyp, fyd || (fyp->cfg.flags & FYPCF_COLLECT_DIAG), err_out,
+ "fy_parse_load_document() failed");
+
+ /* no document, but we're collecting diagnostics */
+ if (!fyd) {
+
+ fyp_error(fyp, "fy_parse_load_document() failed");
+
+ fyp->stream_error = false;
+ fyd = fy_parse_document_create(fyp, NULL);
+ fyp_error_check(fyp, fyd, err_out,
+ "fy_parse_document_create() failed");
+ fyd->parse_error = true;
+
+ /* XXX */
+ goto out;
+ }
+
+ got_stream_end = false;
+ while (!got_stream_end && (fyep = fy_parse_private(fyp)) != NULL) {
+ if (fyep->e.type == FYET_STREAM_END)
+ got_stream_end = true;
+ fy_parse_eventp_recycle(fyp, fyep);
+ }
+
+ if (got_stream_end) {
+ fyep = fy_parse_private(fyp);
+ fyp_error_check(fyp, !fyep, err_out,
+ "more events after stream end");
+ fy_parse_eventp_recycle(fyp, fyep);
+ }
+
+out:
+ fy_parse_cleanup(fyp);
+ return fyd;
+
+err_out:
+ fy_document_destroy(fyd);
+ fy_parse_cleanup(fyp);
+ return NULL;
+}
+
+struct fy_document *fy_document_build_from_string(const struct fy_parse_cfg *cfg,
+ const char *str, size_t len)
+{
+ struct fy_document_build_string_ctx ctx = {
+ .str = str,
+ .len = len,
+ };
+
+ return fy_document_build_internal(cfg, parser_setup_from_string, &ctx);
+}
+
+struct fy_document *fy_document_build_from_malloc_string(const struct fy_parse_cfg *cfg,
+ char *str, size_t len)
+{
+ struct fy_document_build_malloc_string_ctx ctx = {
+ .str = str,
+ .len = len,
+ };
+
+ return fy_document_build_internal(cfg, parser_setup_from_malloc_string, &ctx);
+}
+
+struct fy_document *fy_document_build_from_file(const struct fy_parse_cfg *cfg,
+ const char *file)
+{
+ struct fy_document_build_file_ctx ctx = {
+ .file = file,
+ };
+
+ return fy_document_build_internal(cfg, parser_setup_from_file, &ctx);
+}
+
+struct fy_document *fy_document_build_from_fp(const struct fy_parse_cfg *cfg,
+ FILE *fp)
+{
+ struct fy_document_build_fp_ctx ctx = {
+ .name = NULL,
+ .fp = fp,
+ };
+
+ return fy_document_build_internal(cfg, parser_setup_from_fp, &ctx);
+}
+
+enum fy_node_type fy_node_get_type(struct fy_node *fyn)
+{
+ /* a NULL is a plain scalar node */
+ return fyn ? fyn->type : FYNT_SCALAR;
+}
+
+enum fy_node_style fy_node_get_style(struct fy_node *fyn)
+{
+ /* a NULL is a plain scalar node */
+ return fyn ? fyn->style : FYNS_PLAIN;
+}
+
+bool fy_node_is_null(struct fy_node *fyn)
+{
+ if (!fyn)
+ return true;
+
+ if (fyn->type != FYNT_SCALAR)
+ return false;
+
+ return fyn->scalar == NULL;
+}
+
+bool fy_node_is_attached(struct fy_node *fyn)
+{
+ return fyn ? fyn->attached : false;
+}
+
+struct fy_node *fy_node_get_parent(struct fy_node *fyn)
+{
+ return fyn && !fyn->key_root ? fyn->parent : NULL;
+}
+
+struct fy_node *fy_node_get_document_parent(struct fy_node *fyn)
+{
+ return fyn ? fyn->parent : NULL;
+}
+
+struct fy_token *fy_node_get_tag_token(struct fy_node *fyn)
+{
+ return fyn ? fyn->tag : NULL;
+}
+
+struct fy_token *fy_node_get_scalar_token(struct fy_node *fyn)
+{
+ return fyn && fyn->type == FYNT_SCALAR ? fyn->scalar : NULL;
+}
+
+struct fy_node *fy_node_pair_key(struct fy_node_pair *fynp)
+{
+ return fynp ? fynp->key : NULL;
+}
+
+struct fy_node *fy_node_pair_value(struct fy_node_pair *fynp)
+{
+ return fynp ? fynp->value : NULL;
+}
+
+int fy_node_pair_set_key(struct fy_node_pair *fynp, struct fy_node *fyn)
+{
+ struct fy_node *fyn_map;
+ struct fy_node_pair *fynpi;
+
+ if (!fynp)
+ return -1;
+
+ /* the node must not be attached */
+ if (fyn && fyn->attached)
+ return -1;
+
+ /* sanity check and duplication check */
+ fyn_map = fynp->parent;
+ if (fyn_map) {
+
+ /* (in)sanity check */
+ if (!fy_node_is_mapping(fyn_map))
+ return -1;
+
+ if (fyn_map->xl) {
+ /* either we're already on the parent list (and it's OK) */
+ /* or we're not and we have a duplicate key */
+ fynpi = fy_node_accel_lookup_by_node(fyn_map, fyn);
+ if (fynpi && fynpi != fynp)
+ return -1;
+ /* remove that key */
+ fy_accel_remove(fyn_map->xl, fynp->key);
+ } else {
+ /* check whether the key is a duplicate
+ * skipping ourselves since our key gets replaced
+ */
+ for (fynpi = fy_node_pair_list_head(&fyn_map->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fyn_map->mapping, fynpi)) {
+
+ if (fynpi != fynp && fy_node_compare(fynpi->key, fyn))
+ return -1;
+ }
+ }
+
+ fy_node_mark_synthetic(fyn_map);
+ }
+
+ fy_node_detach_and_free(fynp->key);
+ fynp->key = fyn;
+
+ if (fyn_map && fyn_map->xl)
+ fy_accel_insert(fyn_map->xl, fynp->key, fynp);
+
+ fyn->attached = true;
+
+ return 0;
+}
+
+int fy_node_pair_set_value(struct fy_node_pair *fynp, struct fy_node *fyn)
+{
+ if (!fynp)
+ return -1;
+ /* the node must not be attached */
+ if (fyn && fyn->attached)
+ return -1;
+ fy_node_detach_and_free(fynp->value);
+ fynp->value = fyn;
+ fyn->attached = true;
+
+ if (fynp->parent)
+ fy_node_mark_synthetic(fynp->parent);
+
+ return 0;
+}
+
+struct fy_node *fy_document_root(struct fy_document *fyd)
+{
+ return fyd->root;
+}
+
+const char *fy_node_get_tag(struct fy_node *fyn, size_t *lenp)
+{
+ size_t tmplen;
+
+ if (!lenp)
+ lenp = &tmplen;
+
+ if (!fyn || !fyn->tag) {
+ *lenp = 0;
+ return NULL;
+ }
+
+ return fy_token_get_text(fyn->tag, lenp);
+}
+
+const char *fy_node_get_scalar(struct fy_node *fyn, size_t *lenp)
+{
+ size_t tmplen;
+
+ if (!lenp)
+ lenp = &tmplen;
+
+ if (!fyn || fyn->type != FYNT_SCALAR) {
+ *lenp = 0;
+ return NULL;
+ }
+
+ return fy_token_get_text(fyn->scalar, lenp);
+}
+
+const char *fy_node_get_scalar0(struct fy_node *fyn)
+{
+ if (!fyn || fyn->type != FYNT_SCALAR)
+ return NULL;
+
+ return fy_token_get_text0(fyn->scalar);
+}
+
+size_t fy_node_get_scalar_length(struct fy_node *fyn)
+{
+
+ if (!fyn || fyn->type != FYNT_SCALAR)
+ return 0;
+
+ return fy_token_get_text_length(fyn->scalar);
+}
+
+size_t fy_node_get_scalar_utf8_length(struct fy_node *fyn)
+{
+
+ if (!fyn || fyn->type != FYNT_SCALAR)
+ return 0;
+
+ return fy_token_format_utf8_length(fyn->scalar);
+}
+
+struct fy_node *fy_node_sequence_iterate(struct fy_node *fyn, void **prevp)
+{
+ if (!fyn || fyn->type != FYNT_SEQUENCE || !prevp)
+ return NULL;
+
+ return *prevp = *prevp ? fy_node_next(&fyn->sequence, *prevp) : fy_node_list_head(&fyn->sequence);
+}
+
+struct fy_node *fy_node_sequence_reverse_iterate(struct fy_node *fyn, void **prevp)
+{
+ if (!fyn || fyn->type != FYNT_SEQUENCE || !prevp)
+ return NULL;
+
+ return *prevp = *prevp ? fy_node_prev(&fyn->sequence, *prevp) : fy_node_list_tail(&fyn->sequence);
+}
+
+int fy_node_sequence_item_count(struct fy_node *fyn)
+{
+ struct fy_node *fyni;
+ int count;
+
+ if (!fyn || fyn->type != FYNT_SEQUENCE)
+ return -1;
+
+ count = 0;
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni; fyni = fy_node_next(&fyn->sequence, fyni))
+ count++;
+ return count;
+}
+
+bool fy_node_sequence_is_empty(struct fy_node *fyn)
+{
+ return !fyn || fyn->type != FYNT_SEQUENCE || fy_node_list_empty(&fyn->sequence);
+}
+
+struct fy_node *fy_node_sequence_get_by_index(struct fy_node *fyn, int index)
+{
+ struct fy_node *fyni;
+ void *iterp = NULL;
+
+ if (!fyn || fyn->type != FYNT_SEQUENCE)
+ return NULL;
+
+ if (index >= 0) {
+ do {
+ fyni = fy_node_sequence_iterate(fyn, &iterp);
+ } while (fyni && --index >= 0);
+ } else {
+ do {
+ fyni = fy_node_sequence_reverse_iterate(fyn, &iterp);
+ } while (fyni && ++index < 0);
+ }
+
+ return fyni;
+}
+
+struct fy_node_pair *fy_node_mapping_iterate(struct fy_node *fyn, void **prevp)
+{
+ if (!fyn || fyn->type != FYNT_MAPPING || !prevp)
+ return NULL;
+
+ return *prevp = *prevp ? fy_node_pair_next(&fyn->mapping, *prevp) : fy_node_pair_list_head(&fyn->mapping);
+}
+
+struct fy_node_pair *fy_node_mapping_reverse_iterate(struct fy_node *fyn, void **prevp)
+{
+ if (!fyn || fyn->type != FYNT_MAPPING || !prevp)
+ return NULL;
+
+ return *prevp = *prevp ? fy_node_pair_prev(&fyn->mapping, *prevp) : fy_node_pair_list_tail(&fyn->mapping);
+}
+
+struct fy_node *fy_node_collection_iterate(struct fy_node *fyn, void **prevp)
+{
+ struct fy_node_pair *fynp;
+
+ if (!fyn || !prevp)
+ return NULL;
+
+ switch (fyn->type) {
+ case FYNT_SEQUENCE:
+ return fy_node_sequence_iterate(fyn, prevp);
+
+ case FYNT_MAPPING:
+ fynp = fy_node_mapping_iterate(fyn, prevp);
+ if (!fynp)
+ return NULL;
+ return fynp->value;
+
+ case FYNT_SCALAR:
+ fyn = !*prevp ? fyn : NULL;
+ *prevp = fyn;
+ return fyn;
+ }
+
+ return NULL;
+}
+
+
+int fy_node_mapping_item_count(struct fy_node *fyn)
+{
+ struct fy_node_pair *fynpi;
+ int count;
+
+ if (!fyn || fyn->type != FYNT_MAPPING)
+ return -1;
+
+ count = 0;
+ for (fynpi = fy_node_pair_list_head(&fyn->mapping); fynpi; fynpi = fy_node_pair_next(&fyn->mapping, fynpi))
+ count++;
+ return count;
+}
+
+bool fy_node_mapping_is_empty(struct fy_node *fyn)
+{
+ return !fyn || fyn->type != FYNT_MAPPING || fy_node_pair_list_empty(&fyn->mapping);
+}
+
+struct fy_node_pair *fy_node_mapping_get_by_index(struct fy_node *fyn, int index)
+{
+ struct fy_node_pair *fynpi;
+ void *iterp = NULL;
+
+ if (!fyn || fyn->type != FYNT_MAPPING)
+ return NULL;
+
+ if (index >= 0) {
+ do {
+ fynpi = fy_node_mapping_iterate(fyn, &iterp);
+ } while (fynpi && --index >= 0);
+ } else {
+ do {
+ fynpi = fy_node_mapping_reverse_iterate(fyn, &iterp);
+ } while (fynpi && ++index < 0);
+ }
+
+ return fynpi;
+}
+
+struct fy_node_pair *
+fy_node_mapping_lookup_pair_by_simple_key(struct fy_node *fyn,
+ const char *key, size_t len)
+{
+ struct fy_node_pair *fynpi;
+ struct fy_node *fyn_scalar;
+
+ if (!fyn || fyn->type != FYNT_MAPPING || !key)
+ return NULL;
+
+ if (len == (size_t)-1)
+ len = strlen(key);
+
+ if (fyn->xl) {
+ fyn_scalar = fy_node_create_scalar(fyn->fyd, key, len);
+ if (!fyn_scalar)
+ return NULL;
+
+ fynpi = fy_node_accel_lookup_by_node(fyn, fyn_scalar);
+
+ fy_node_free(fyn_scalar);
+
+ if (fynpi)
+ return fynpi;
+ } else {
+ for (fynpi = fy_node_pair_list_head(&fyn->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fyn->mapping, fynpi)) {
+
+ if (!fy_node_is_scalar(fynpi->key) || fy_node_is_alias(fynpi->key))
+ continue;
+
+ if (!fynpi->key && len == 0)
+ return fynpi;
+
+ if (fynpi->key && !fy_token_memcmp(fynpi->key->scalar, key, len))
+ return fynpi;
+ }
+ }
+
+ return NULL;
+}
+
+struct fy_node *
+fy_node_mapping_lookup_value_by_simple_key(struct fy_node *fyn,
+ const char *key, size_t len)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_lookup_pair_by_simple_key(fyn, key, len);
+ return fynp ? fy_node_pair_value(fynp) : NULL;
+}
+
+struct fy_node_pair *
+fy_node_mapping_lookup_pair_by_null_key(struct fy_node *fyn)
+{
+ struct fy_node_pair *fynpi;
+
+ if (!fyn || fyn->type != FYNT_MAPPING)
+ return NULL;
+
+ /* no acceleration for NULLs */
+ for (fynpi = fy_node_pair_list_head(&fyn->mapping); fynpi;
+ fynpi = fy_node_pair_next(&fyn->mapping, fynpi)) {
+
+ if (fy_node_is_null(fynpi->key))
+ return fynpi;
+ }
+
+ return NULL;
+}
+
+struct fy_node *
+fy_node_mapping_lookup_value_by_null_key(struct fy_node *fyn)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_lookup_pair_by_null_key(fyn);
+ return fynp ? fy_node_pair_value(fynp) : NULL;
+}
+
+const char *
+fy_node_mapping_lookup_scalar_by_simple_key(struct fy_node *fyn, size_t *lenp,
+ const char *key, size_t keylen)
+{
+ struct fy_node *fyn_value;
+
+ fyn_value = fy_node_mapping_lookup_value_by_simple_key(fyn, key, keylen);
+ if (!fyn_value || !fy_node_is_scalar(fyn_value))
+ return NULL;
+ return fy_node_get_scalar(fyn_value, lenp);
+}
+
+const char *
+fy_node_mapping_lookup_scalar0_by_simple_key(struct fy_node *fyn,
+ const char *key, size_t keylen)
+{
+ struct fy_node *fyn_value;
+
+ fyn_value = fy_node_mapping_lookup_value_by_simple_key(fyn, key, keylen);
+ if (!fyn_value || !fy_node_is_scalar(fyn_value))
+ return NULL;
+ return fy_node_get_scalar0(fyn_value);
+}
+
+struct fy_node *fy_node_mapping_lookup_value_by_key(struct fy_node *fyn, struct fy_node *fyn_key)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_lookup_pair(fyn, fyn_key);
+ return fynp ? fynp->value : NULL;
+}
+
+struct fy_node *fy_node_mapping_lookup_key_by_key(struct fy_node *fyn, struct fy_node *fyn_key)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_lookup_pair(fyn, fyn_key);
+ return fynp ? fynp->key : NULL;
+}
+
+struct fy_node_pair *
+fy_node_mapping_lookup_pair_by_string(struct fy_node *fyn, const char *key, size_t len)
+{
+ struct fy_document *fyd;
+ struct fy_node_pair *fynp;
+
+ /* try quick and dirty simple scan */
+ if (is_simple_key(key, len))
+ return fy_node_mapping_lookup_pair_by_simple_key(fyn, key, len);
+
+ fyd = fy_document_build_from_string(NULL, key, len);
+ if (!fyd)
+ return NULL;
+
+ fynp = fy_node_mapping_lookup_pair(fyn, fy_document_root(fyd));
+
+ fy_document_destroy(fyd);
+
+ return fynp;
+}
+
+struct fy_node *
+fy_node_mapping_lookup_by_string(struct fy_node *fyn,
+ const char *key, size_t len)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_lookup_pair_by_string(fyn, key, len);
+ return fynp ? fynp->value : NULL;
+}
+
+struct fy_node *
+fy_node_mapping_lookup_value_by_string(struct fy_node *fyn,
+ const char *key, size_t len)
+{
+ return fy_node_mapping_lookup_by_string(fyn, key, len);
+}
+
+struct fy_node *
+fy_node_mapping_lookup_key_by_string(struct fy_node *fyn,
+ const char *key, size_t len)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_lookup_pair_by_string(fyn, key, len);
+ return fynp ? fynp->key : NULL;
+}
+
+bool fy_node_is_empty(struct fy_node *fyn)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp;
+ struct fy_atom *atom;
+
+ /* skip if no value node or token */
+ if (!fyn)
+ return true;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ atom = fy_token_atom(fyn->scalar);
+ if (atom && !atom->size0 && !atom->empty)
+ return false;
+ break;
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+ if (!fy_node_is_empty(fyni))
+ return false;
+ }
+ break;
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp;
+ fynp = fy_node_pair_next(&fyn->mapping, fynp)) {
+ if (!fy_node_is_empty(fynp->value))
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+
+#define fy_node_walk_ctx_create_a(_max_depth, _mark, _res) \
+ do { \
+ unsigned int __max_depth = (_max_depth); \
+ struct fy_node_walk_ctx *_ctx; \
+ \
+ _ctx = FY_ALLOCA(sizeof(*_ctx) + sizeof(struct fy_node *) * __max_depth); \
+ _ctx->max_depth = _max_depth; \
+ _ctx->next_slot = 0; \
+ _ctx->mark = (_mark); \
+ *(_res) = _ctx; \
+ } while(false)
+
+static inline void fy_node_walk_mark_start(struct fy_node_walk_ctx *ctx)
+{
+ ctx->next_slot = 0;
+}
+
+static inline void fy_node_walk_mark_end(struct fy_node_walk_ctx *ctx)
+{
+ struct fy_node *fyn;
+
+ while (ctx->next_slot > 0) {
+ fyn = ctx->marked[--ctx->next_slot];
+ fyn->marks &= ~ctx->mark;
+ }
+}
+
+/* fyn is guaranteed to be non NULL and an alias */
+static inline bool fy_node_walk_mark(struct fy_node_walk_ctx *ctx, struct fy_node *fyn)
+{
+ struct fy_document *fyd = fyn->fyd;
+ struct fy_token *fyt = NULL;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ fyt = fyn->scalar;
+ break;
+ case FYNT_SEQUENCE:
+ fyt = fyn->sequence_start;
+ break;
+ case FYNT_MAPPING:
+ fyt = fyn->mapping_start;
+ break;
+ }
+
+ /* depth error */
+ FYD_TOKEN_ERROR_CHECK(fyd, fyt, FYEM_DOC,
+ ctx->next_slot < ctx->max_depth, err_out,
+ "max recursion depth exceeded (%u)", ctx->max_depth);
+
+ /* mark found, loop */
+ FYD_TOKEN_ERROR_CHECK(fyd, fyt, FYEM_DOC,
+ !(fyn->marks & ctx->mark), err_out,
+ "cyclic reference detected");
+
+ fyn->marks |= ctx->mark;
+ ctx->marked[ctx->next_slot++] = fyn;
+
+ return true;
+
+err_out:
+ return false;
+}
+
+static struct fy_node *
+fy_node_follow_aliases(struct fy_node *fyn, enum fy_node_walk_flags flags, bool single)
+{
+ enum fy_node_walk_flags ptr_flags;
+ struct fy_ptr_node_list nl;
+ struct fy_ptr_node *fypn;
+
+ if (!fyn || !fy_node_is_alias(fyn) || !(flags & FYNWF_FOLLOW))
+ return fyn;
+
+ ptr_flags = flags & FYNWF_PTR(FYNWF_PTR_MASK);
+ if (ptr_flags != FYNWF_PTR_YAML && ptr_flags != FYNWF_PTR_YPATH)
+ return fyn;
+
+ fy_ptr_node_list_init(&nl);
+
+ while (fyn && fy_node_is_alias(fyn)) {
+
+ // fprintf(stderr, "%s: %s\n", __func__, fy_node_get_path_FY_ALLOCA(fyn));
+
+ /* check for loops */
+ if (fy_ptr_node_list_contains(&nl, fyn)) {
+ fyn = NULL;
+ break;
+ }
+
+ /* out of memory? */
+ fypn = fy_ptr_node_create(fyn);
+ if (!fypn) {
+ fyn = NULL;
+ break;
+ }
+ fy_ptr_node_list_add_tail(&nl, fypn);
+
+ fyn = fy_node_follow_alias(fyn, flags);
+
+ if (single)
+ break;
+ }
+
+ /* release */
+ while ((fypn = fy_ptr_node_list_pop(&nl)) != NULL)
+ fy_ptr_node_destroy(fypn);
+
+ return fyn;
+}
+
+struct fy_node *fy_node_resolve_alias(struct fy_node *fyn)
+{
+ enum fy_node_walk_flags flags;
+
+ if (!fyn)
+ return NULL;
+
+ flags = FYNWF_FOLLOW | FYNWF_MAXDEPTH_DEFAULT | FYNWF_MARKER_DEFAULT;
+ if (fyn->fyd->parse_cfg.flags & FYPCF_YPATH_ALIASES)
+ flags |= FYNWF_PTR_YPATH;
+ else
+ flags |= FYNWF_PTR_YAML;
+ return fy_node_follow_aliases(fyn, flags, false);
+}
+
+struct fy_node *fy_node_dereference(struct fy_node *fyn)
+{
+ enum fy_node_walk_flags flags;
+
+ if (!fyn || !fy_node_is_alias(fyn))
+ return NULL;
+
+ flags = FYNWF_FOLLOW | FYNWF_MAXDEPTH_DEFAULT | FYNWF_MARKER_DEFAULT;
+ if (fyn->fyd->parse_cfg.flags & FYPCF_YPATH_ALIASES)
+ flags |= FYNWF_PTR_YPATH;
+ else
+ flags |= FYNWF_PTR_YAML;
+ return fy_node_follow_aliases(fyn, flags, true);
+}
+
+static struct fy_node *
+fy_node_by_path_internal(struct fy_node *fyn,
+ const char *path, size_t pathlen,
+ enum fy_node_walk_flags flags)
+{
+ enum fy_node_walk_flags ptr_flags;
+ struct fy_node *fynt, *fyni;
+ const char *s, *e, *ss, *ee;
+ char *end_idx, *json_key, *t, *p, *uri_path;
+ char c;
+ int idx, rlen;
+ size_t len, json_key_len, uri_path_len;
+ bool has_json_key_esc;
+ uint8_t code[4];
+ int code_length;
+ bool trailing_slash;
+
+ if (!fyn || !path)
+ return NULL;
+
+ ptr_flags = flags & FYNWF_PTR(FYNWF_PTR_MASK);
+ if (ptr_flags == FYNWF_PTR_YPATH)
+ return fy_node_by_ypath(fyn, path, pathlen);
+
+ s = path;
+ if (pathlen == (size_t)-1)
+ pathlen = strlen(path);
+ e = s + pathlen;
+
+ /* a trailing slash works just like unix and symbolic links
+ * if it does not exist no symbolic link lookups are performed
+ * at the end of the operation.
+ * if it exists they are followed upon resolution
+ */
+ trailing_slash = pathlen > 0 && path[pathlen - 1] == '/';
+
+ /* and continue on path lookup with the rest */
+
+ /* skip all prefixed / */
+ switch (ptr_flags) {
+ default:
+ case FYNWF_PTR_YAML:
+ while (s < e && *s == '/')
+ s++;
+ /* for a last component / always match this one */
+ if (s >= e)
+ goto out;
+ break;
+
+ case FYNWF_PTR_JSON:
+ /* "" -> everything here */
+ if (s == e)
+ return fyn;
+ /* it must have a separator here */
+ if (*s != '/')
+ return NULL;
+ s++;
+ break;
+
+ case FYNWF_PTR_RELJSON:
+ break;
+ }
+
+ /* fyd_notice(fyn->fyd, "%s:%d following alias @%s \"%.*s\"",
+ __func__, __LINE__, fy_node_get_path(fyn), (int)(e - s), s); */
+ fyn = fy_node_follow_aliases(fyn, flags, true);
+
+ /* scalar can be only last element in the path (it has no key) */
+ if (fy_node_is_scalar(fyn)) {
+ if (*s)
+ fyn = NULL; /* not end of the path - fail */
+ goto out;
+ }
+
+ /* for a sequence the only allowed key is [n] where n is the index to follow */
+ if (fy_node_is_sequence(fyn)) {
+
+ c = -1;
+ switch (ptr_flags) {
+ default:
+ case FYNWF_PTR_YAML:
+ while (s < e && isspace(*s))
+ s++;
+
+ c = *s;
+ if (c == '[')
+ s++;
+ else if (!isdigit(c) && c != '-')
+ return NULL;
+
+ idx = (int)strtol(s, &end_idx, 10);
+
+ /* no digits found at all */
+ if (idx == 0 && end_idx == s)
+ return NULL;
+
+ s = end_idx;
+
+ while (s < e && isspace(*s))
+ s++;
+
+ if (c == '[' && *s++ != ']')
+ return NULL;
+
+ while (s < e && isspace(*s))
+ s++;
+
+ break;
+
+ case FYNWF_PTR_JSON:
+ case FYNWF_PTR_RELJSON:
+
+ /* special array end - always fails */
+ if (*s == '-')
+ return NULL;
+
+ idx = (int)strtol(s, &end_idx, 10);
+
+ /* no digits found at all */
+ if (idx == 0 && end_idx == s)
+ return NULL;
+
+ /* no negatives */
+ if (idx < 0)
+ return NULL;
+
+ s = end_idx;
+
+ if (s < e && *s == '/')
+ s++;
+
+ break;
+ }
+
+ len = e - s;
+
+ fyn = fy_node_sequence_get_by_index(fyn, idx);
+ if (trailing_slash)
+ fyn = fy_node_follow_aliases(fyn, flags, false);
+ fyn = fy_node_by_path_internal(fyn, s, len, flags);
+ goto out;
+ }
+
+ /* be a little bit paranoid */
+ assert(fy_node_is_mapping(fyn));
+
+ path = s;
+ pathlen = (size_t)(e - s);
+
+ switch (ptr_flags) {
+ default:
+ case FYNWF_PTR_YAML:
+
+ /* scan ahead for the end of the path component
+ * note that we don't do UTF8 here, because all the
+ * escapes are regular ascii characters, i.e.
+ * '/', '*', '&', '.', '{', '}', '[', ']' and '\\'
+ */
+
+ while (s < e) {
+ c = *s;
+ /* end of path component? */
+ if (c == '/')
+ break;
+ s++;
+
+ if (c == '\\') {
+ /* it must be a valid escape */
+ if (s >= e || !strchr("/*&.{}[]\\", *s))
+ return NULL;
+ s++;
+ } else if (c == '"') {
+ while (s < e && *s != '"') {
+ c = *s++;
+ if (c == '\\' && (s < e && *s == '"'))
+ s++;
+ }
+ /* not a normal double quote end */
+ if (s >= e || *s != '"')
+ return NULL;
+ s++;
+ } else if (c == '\'') {
+ while (s < e && *s != '\'') {
+ c = *s++;
+ if (c == '\'' && (s < e && *s == '\''))
+ s++;
+ }
+ /* not a normal single quote end */
+ if (s >= e || *s != '\'')
+ return NULL;
+ s++;
+ }
+ }
+ len = s - path;
+
+ fynt = fyn;
+ fyn = fy_node_mapping_lookup_by_string(fyn, path, len);
+
+ /* failed! last ditch attempt, is there a merge key? */
+ if (!fyn && fynt && (flags & FYNWF_FOLLOW) && ptr_flags == FYNWF_PTR_YAML) {
+ fyn = fy_node_mapping_lookup_by_string(fynt, "<<", 2);
+ if (!fyn)
+ goto out;
+
+ if (fy_node_is_alias(fyn)) {
+
+ /* single alias '<<: *foo' */
+ fyn = fy_node_mapping_lookup_by_string(
+ fy_node_follow_aliases(fyn, flags, false), path, len);
+
+ } else if (fy_node_is_sequence(fyn)) {
+
+ /* multi aliases '<<: [ *foo, *bar ]' */
+ fynt = fyn;
+ for (fyni = fy_node_list_head(&fynt->sequence); fyni;
+ fyni = fy_node_next(&fynt->sequence, fyni)) {
+ if (!fy_node_is_alias(fyni))
+ continue;
+ fyn = fy_node_mapping_lookup_by_string(
+ fy_node_follow_aliases(fyni, flags, false),
+ path, len);
+ if (fyn)
+ break;
+ }
+ } else
+ fyn = NULL;
+ }
+ break;
+
+ case FYNWF_PTR_JSON:
+ case FYNWF_PTR_RELJSON:
+
+ has_json_key_esc = false;
+ while (s < e) {
+ c = *s;
+ /* end of path component? */
+ if (c == '/')
+ break;
+ s++;
+ if (c == '~')
+ has_json_key_esc = true;
+ }
+ len = s - path;
+
+ if (has_json_key_esc) {
+ /* note that the escapes reduce the length, so allocating the
+ * same size is guaranteed safe */
+ json_key = FY_ALLOCA(len + 1);
+
+ ss = path;
+ ee = s;
+ t = json_key;
+ while (ss < ee) {
+ if (*ss != '~') {
+ *t++ = *ss++;
+ continue;
+ }
+ /* unterminated ~ escape, or neither ~0, ~1 */
+ if (ss + 1 >= ee || (ss[1] < '0' && ss[1] > '1'))
+ return NULL;
+ *t++ = ss[1] == '0' ? '~' : '/';
+ ss += 2;
+ }
+ json_key_len = t - json_key;
+
+ path = json_key;
+ len = json_key_len;
+ }
+
+ /* URI encoded escaped */
+ if ((flags & FYNWF_URI_ENCODED) && memchr(path, '%', len)) {
+ /* escapes shrink, so safe to allocate as much */
+ uri_path = FY_ALLOCA(len + 1);
+
+ ss = path;
+ ee = path + len;
+ t = uri_path;
+ while (ss < ee) {
+ /* copy run until '%' or end */
+ p = memchr(ss, '%', ee - ss);
+ rlen = (p ? p : ee) - ss;
+ memcpy(t, ss, rlen);
+ ss += rlen;
+ t += rlen;
+
+ /* if end, break */
+ if (!p)
+ break;
+
+ /* collect a utf8 character sequence */
+ code_length = sizeof(code);
+ ss = fy_uri_esc(ss, ee - ss, code, &code_length);
+ if (!ss) {
+ /* bad % escape sequence */
+ return NULL;
+ }
+ memcpy(t, code, code_length);
+ t += code_length;
+ }
+ uri_path_len = t - uri_path;
+
+ path = uri_path;
+ len = uri_path_len;
+ }
+
+ fynt = fyn;
+ fyn = fy_node_mapping_lookup_value_by_simple_key(fyn, path, len);
+ break;
+ }
+
+ len = e - s;
+
+ if (len > 0 && trailing_slash) {
+ /* fyd_notice(fyn->fyd, "%s:%d following alias @%s \"%.*s\"",
+ __func__, __LINE__, fy_node_get_path(fyn), (int)(e - s), s); */
+ fyn = fy_node_follow_aliases(fyn, flags, true);
+ }
+
+ fyn = fy_node_by_path_internal(fyn, s, len, flags);
+
+out:
+ len = e - s;
+
+ if (len > 0 && trailing_slash) {
+ /* fyd_notice(fyn->fyd, "%s:%d following alias @%s \"%.*s\"",
+ __func__, __LINE__, fy_node_get_path(fyn), (int)(e - s), s); */
+ fyn = fy_node_follow_aliases(fyn, flags, true);
+ }
+
+ return fyn;
+}
+
+struct fy_node *fy_node_by_path(struct fy_node *fyn,
+ const char *path, size_t len,
+ enum fy_node_walk_flags flags)
+{
+ struct fy_document *fyd;
+ struct fy_anchor *fya;
+ const char *s, *e, *t, *anchor;
+ size_t alen;
+ char c;
+ int idx, w;
+ char *end_idx;
+
+ if (!fyn || !path)
+ return NULL;
+
+ if (len == (size_t)-1)
+ len = strlen(path);
+
+ /* verify that the path string is well formed UTF8 */
+ s = path;
+ e = s + len;
+
+ fyd = fyn->fyd;
+ while (s < e) {
+ c = fy_utf8_get(s, e - s, &w);
+ if (c < 0) {
+ fyd_error(fyd, "fy_node_by_path() malformed path string\n");
+ return NULL;
+ }
+ s += w;
+ }
+
+ /* rewind */
+ s = path;
+
+ /* if it's a YPATH, just punt to that method */
+ if ((flags & FYNWF_PTR(FYNWF_PTR_MASK)) == FYNWF_PTR_YPATH)
+ return fy_node_by_ypath(fyn, path, len);
+
+ /* fyd_notice(fyn->fyd, "%s: %.*s", __func__, (int)(len), s); */
+
+ /* first path component may be an alias */
+ if ((flags & FYNWF_FOLLOW) && fyn && path) {
+ while (s < e && isspace(*s))
+ s++;
+
+ if (s >= e || *s != '*')
+ goto regular_path_lookup;
+
+ s++;
+
+ c = -1;
+ for (t = s; t < e; t++) {
+ c = *t;
+ /* it ends on anything non alias */
+ if (c == '[' || c == ']' ||
+ c == '{' || c == '}' ||
+ c == ',' || c == ' ' || c == '\t' ||
+ c == '/')
+ break;
+ }
+
+ /* bad alias form for path */
+ if (c == '[' || c == ']' || c == '{' || c == '}' || c == ',')
+ return NULL;
+
+ anchor = s;
+ alen = t - s;
+
+ if (alen) {
+ /* we must be terminated by '/' or space followed by '/' */
+ /* strip until spaces and '/' end */
+ while (t < e && (*t == ' ' || *t == '\t'))
+ t++;
+
+ while (t < e && *t == '/')
+ t++;
+
+ /* update path */
+ path = t;
+ len = e - t;
+
+ /* fyd_notice(fyn->fyd, "%s: looking up anchor=%.*s", __func__, (int)(alen), anchor); */
+
+ /* lookup anchor */
+ fya = fy_document_lookup_anchor(fyn->fyd, anchor, alen);
+ if (!fya) {
+ /* fyd_notice(fyn->fyd, "%s: failed to lookup anchor=%.*s", __func__, (int)(alen), anchor); */
+ return NULL;
+ }
+
+ /* fyd_notice(fyn->fyd, "%s: found anchor=%.*s at %s",
+ __func__, (int)(alen), anchor, fy_node_get_path(fya->fyn)); */
+
+ /* nothing more? we're done */
+ if (*path == '\0')
+ return fya->fyn;
+
+ /* anchor found... all good */
+
+ fyn = fya->fyn;
+ } else {
+ /* no anchor it must be of the form *\/ */
+
+ path = s;
+ len = e - s;
+ }
+
+ /* fyd_notice(fyn->fyd, "%s: continuing looking for %.*s",
+ __func__, (int)(len), path); */
+
+ }
+
+regular_path_lookup:
+
+ /* if it's a relative json pointer... */
+ if ((flags & FYNWF_PTR(FYNWF_PTR_MASK)) == FYNWF_PTR_RELJSON) {
+
+ /* it must at least be one digit */
+ if (len == 0)
+ return NULL;
+
+ idx = (int)strtol(path, &end_idx, 10);
+
+ /* at least one digit must exist */
+ if (idx == 0 && path == end_idx)
+ return NULL;
+
+ e = path + len;
+ len = e - end_idx;
+ path = end_idx;
+
+ /* we don't do the trailing # here */
+ if (len == 1 && *path == '#')
+ return NULL;
+
+ while (idx-- > 0)
+ fyn = fy_node_get_parent(fyn);
+
+ /* convert to regular json pointer from now on */
+ flags &= ~FYNWF_PTR(FYNWF_PTR_MASK);
+ flags |= FYNWF_PTR_JSON;
+ }
+
+ return fy_node_by_path_internal(fyn, path, len, flags);
+}
+
+static char *
+fy_node_get_reference_internal(struct fy_node *fyn_base, struct fy_node *fyn, bool near)
+{
+ struct fy_anchor *fya;
+ const char *path;
+ char *path2, *path3;
+ const char *text;
+ size_t len;
+
+ if (!fyn)
+ return NULL;
+
+ path2 = NULL;
+
+ /* if the node has an anchor use it (ie return *foo) */
+ if (!fyn_base && (fya = fy_node_get_anchor(fyn)) != NULL) {
+ text = fy_anchor_get_text(fya, &len);
+ if (!text)
+ return NULL;
+ path2 = FY_ALLOCA(1 + len + 1);
+ path2[0] = '*';
+ memcpy(path2 + 1, text, len);
+ path2[len + 1] = '\0';
+
+ } else {
+
+ fya = fyn_base ? fy_node_get_anchor(fyn_base) : NULL;
+ if (!fya && near)
+ fya = fy_node_get_nearest_anchor(fyn);
+ if (!fya) {
+ /* no anchor, direct reference (ie return *\/foo\/bar */
+ fy_node_get_path_alloca(fyn, &path);
+ if (!*path)
+ return NULL;
+ path2 = FY_ALLOCA(1 + strlen(path) + 1);
+ path2[0] = '*';
+ strcpy(path2 + 1, path);
+ } else {
+ text = fy_anchor_get_text(fya, &len);
+ if (!text)
+ return NULL;
+ if (fy_anchor_node(fya) != fyn) {
+ fy_node_get_path_relative_to_alloca(fy_anchor_node(fya), fyn, &path);
+ if (*path) {
+ /* we have a relative path */
+ path2 = FY_ALLOCA(1 + len + 1 + strlen(path) + 1);
+ path2[0] = '*';
+ memcpy(path2 + 1, text, len);
+ path2[len + 1] = '/';
+ memcpy(1 + path2 + len + 1, path, strlen(path) + 1);
+ } else {
+ /* absolute path */
+ fy_node_get_path_alloca(fyn, &path);
+ if (!*path)
+ return NULL;
+ path2 = FY_ALLOCA(1 + strlen(path) + 1);
+ path2[0] = '*';
+ strcpy(path2 + 1, path);
+ }
+ } else {
+ path2 = FY_ALLOCA(1 + len + 1);
+ path2[0] = '*';
+ memcpy(path2 + 1, text, len);
+ path2[len + 1] = '\0';
+ }
+ }
+ }
+
+ if (!path2)
+ return NULL;
+
+ path3 = strdup(path2);
+ if (!path3)
+ return NULL;
+
+ return path3;
+}
+
+char *fy_node_get_reference(struct fy_node *fyn)
+{
+ return fy_node_get_reference_internal(NULL, fyn, false);
+}
+
+struct fy_node *fy_node_create_reference(struct fy_node *fyn)
+{
+ struct fy_node *fyn_ref;
+ char *path, *alias;
+
+ path = fy_node_get_reference(fyn);
+ if (!path)
+ return NULL;
+
+ alias = path;
+ if (*alias == '*')
+ alias++;
+
+ fyn_ref = fy_node_create_alias_copy(fy_node_document(fyn), alias, FY_NT);
+
+ free(path);
+
+ return fyn_ref;
+}
+
+char *fy_node_get_relative_reference(struct fy_node *fyn_base, struct fy_node *fyn)
+{
+ return fy_node_get_reference_internal(fyn_base, fyn, false);
+}
+
+struct fy_node *fy_node_create_relative_reference(struct fy_node *fyn_base, struct fy_node *fyn)
+{
+ struct fy_node *fyn_ref;
+ char *path, *alias;
+
+ path = fy_node_get_relative_reference(fyn_base, fyn);
+ if (!path)
+ return NULL;
+
+ alias = path;
+ if (*alias == '*')
+ alias++;
+
+ fyn_ref = fy_node_create_alias_copy(fy_node_document(fyn), alias, FY_NT);
+
+ free(path);
+
+ return fyn_ref;
+}
+
+bool fy_check_ref_loop(struct fy_document *fyd, struct fy_node *fyn,
+ enum fy_node_walk_flags flags,
+ struct fy_node_walk_ctx *ctx)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi;
+ struct fy_node_walk_ctx *ctxn;
+ bool ret;
+
+ if (!fyn)
+ return false;
+
+ /* visited? no need to check */
+ if (fyn->marks & FY_BIT(FYNWF_VISIT_MARKER))
+ return false;
+
+ /* marked node, it's a loop */
+ if (ctx && !fy_node_walk_mark(ctx, fyn))
+ return true;
+
+ ret = false;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+
+ /* if it's not an alias, we're done */
+ if (!fy_node_is_alias(fyn))
+ break;
+
+ ctxn = ctx;
+ if (!ctxn)
+ fy_node_walk_ctx_create_a(
+ fy_node_walk_max_depth_from_flags(flags), FYNWF_REF_MARKER, &ctxn);
+
+
+ if (!ctx) {
+ fy_node_walk_mark_start(ctxn);
+
+ /* mark this node */
+ fy_node_walk_mark(ctxn, fyn);
+ }
+
+ fyni = fy_node_follow_alias(fyn, flags);
+
+ ret = fy_check_ref_loop(fyd, fyni, flags, ctxn);
+
+ if (!ctx)
+ fy_node_walk_mark_end(ctxn);
+
+ if (ret)
+ break;
+
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ ret = fy_check_ref_loop(fyd, fyni, flags, ctx);
+ if (ret)
+ break;
+ }
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ ret = fy_check_ref_loop(fyd, fynp->key, flags, ctx);
+ if (ret)
+ break;
+
+ ret = fy_check_ref_loop(fyd, fynp->value, flags, ctx);
+ if (ret)
+ break;
+ }
+ break;
+ }
+
+ /* mark as visited */
+ fyn->marks |= FY_BIT(FYNWF_VISIT_MARKER);
+
+ return ret;
+}
+
+char *fy_node_get_parent_address(struct fy_node *fyn)
+{
+ struct fy_node *parent, *fyni;
+ struct fy_node_pair *fynp;
+ struct fy_node *fyna;
+ char *path = NULL;
+ const char *str;
+ size_t len;
+ int idx;
+ bool is_key_root;
+ int ret;
+ const char *fmt;
+ char *new_path, *old_path;
+
+ if (!fyn)
+ return NULL;
+
+ parent = fy_node_get_document_parent(fyn);
+ if (!parent)
+ return NULL;
+
+ if (fy_node_is_sequence(parent)) {
+
+ /* for a sequence, find the index */
+ idx = 0;
+ for (fyni = fy_node_list_head(&parent->sequence); fyni;
+ fyni = fy_node_next(&parent->sequence, fyni)) {
+ if (fyni == fyn)
+ break;
+ idx++;
+ }
+
+ if (!fyni)
+ return NULL;
+
+ ret = asprintf(&path, "%d", idx);
+ if (ret == -1)
+ return NULL;
+ }
+
+ if (fy_node_is_mapping(parent)) {
+
+ is_key_root = fyn->key_root;
+
+ idx = 0;
+ fyna = NULL;
+ for (fynp = fy_node_pair_list_head(&parent->mapping); fynp;
+ fynp = fy_node_pair_next(&parent->mapping, fynp)) {
+
+ if ((!is_key_root && fynp->value == fyn) || (is_key_root && fynp->key == fyn))
+ break;
+ idx++;
+ }
+
+ if (!fynp)
+ return NULL;
+
+ fyna = fynp->key;
+ if (!fyna)
+ return NULL;
+
+ /* if key is a plain scalar try to not use a complex style (even for quoted) */
+ if (fyna && fy_node_is_scalar(fyna) && !fy_node_is_alias(fyna) &&
+ (str = fy_token_get_scalar_path_key(fyna->scalar, &len)) != NULL) {
+
+ fmt = !is_key_root ? "%.*s" : ".key(%.*s)";
+ ret = asprintf(&path, fmt, (int)len, str);
+ if (ret == -1)
+ return NULL;
+
+ } else {
+
+ /* something complex, emit it */
+ path = fy_emit_node_to_string(fyna,
+ FYECF_MODE_FLOW_ONELINE | FYECF_WIDTH_INF |
+ FYECF_STRIP_LABELS | FYECF_STRIP_TAGS |
+ FYECF_NO_ENDING_NEWLINE);
+ if (!path)
+ return NULL;
+
+ if (is_key_root) {
+ old_path = path;
+ ret = asprintf(&new_path, ".key(%s)", path);
+ if (ret == -1) {
+ free(path);
+ return NULL;
+ }
+ free(old_path);
+ path = new_path;
+ }
+ }
+ }
+
+ return path;
+}
+
+char *fy_node_get_path(struct fy_node *fyn)
+{
+ struct path_track {
+ struct path_track *prev;
+ char *path;
+ };
+ struct path_track *track, *newtrack;
+ char *path, *s, *path_mem;
+ size_t len;
+ struct fy_node *parent;
+
+ if (!fyn)
+ return NULL;
+
+ /* easy on the root */
+ parent = fy_node_get_document_parent(fyn);
+ if (!parent) {
+ path_mem = strdup("/");
+ return path_mem;
+ }
+
+ track = NULL;
+ len = 0;
+ while ((path = fy_node_get_parent_address(fyn))) {
+ newtrack = FY_ALLOCA(sizeof(*newtrack));
+ newtrack->prev = track;
+ newtrack->path = path;
+
+ track = newtrack;
+
+ len += strlen(path) + 1;
+
+ fyn = fy_node_get_document_parent(fyn);
+ }
+ len += 2;
+
+ path_mem = malloc(len);
+
+ s = path_mem;
+
+ while (track) {
+ len = strlen(track->path);
+ if (s) {
+ *s++ = '/';
+ memcpy(s, track->path, len);
+ s += len;
+ }
+ free(track->path);
+ track = track->prev;
+ }
+
+ if (s)
+ *s = '\0';
+
+ return path_mem;
+}
+
+char *fy_node_get_path_relative_to(struct fy_node *fyn_parent, struct fy_node *fyn)
+{
+ char *path, *ppath, *path2, *path_ret;
+ size_t pathlen, ppathlen;
+ struct fy_node *ni, *nj;
+
+ if (!fyn)
+ return NULL;
+
+ /* must be on the same document */
+ if (fyn_parent && (fyn_parent->fyd != fyn->fyd))
+ return NULL;
+
+ if (!fyn_parent)
+ fyn_parent = fyn->fyd->root;
+
+ /* verify that it's a parent */
+ ni = fyn;
+ while ((nj = fy_node_get_parent(ni)) != NULL && nj != fyn_parent)
+ ni = nj;
+
+ /* not a parent, illegal */
+ if (!nj)
+ return NULL;
+
+ /* here we go... */
+ path = "";
+ pathlen = 0;
+
+ ni = fyn;
+ while ((nj = fy_node_get_parent(ni)) != NULL) {
+ ppath = fy_node_get_parent_address(ni);
+ if (!ppath)
+ return NULL;
+
+ ppathlen = strlen(ppath);
+
+ if (pathlen > 0) {
+ path2 = FY_ALLOCA(pathlen + 1 + ppathlen + 1);
+ memcpy(path2, ppath, ppathlen);
+ path2[ppathlen] = '/';
+ memcpy(path2 + ppathlen + 1, path, pathlen);
+ path2[ppathlen + 1 + pathlen] = '\0';
+ } else {
+ path2 = FY_ALLOCA(ppathlen + 1);
+ memcpy(path2, ppath, ppathlen);
+ path2[ppathlen] = '\0';
+ }
+
+ path = path2;
+ pathlen = strlen(path);
+
+ free(ppath);
+ ni = nj;
+
+ if (ni == fyn_parent)
+ break;
+ }
+
+ path_ret = strdup(path);
+ return path_ret;
+}
+
+char *fy_node_get_short_path(struct fy_node *fyn)
+{
+ struct fy_node *fyn_anchor;
+ struct fy_anchor *fya;
+ const char *text;
+ size_t len;
+ const char *str;
+ char *path;
+
+ if (!fyn)
+ return NULL;
+
+ /* get the nearest anchor traversing upwards */
+ fya = fy_node_get_nearest_anchor(fyn);
+ if (!fya)
+ return fy_node_get_path(fyn);
+
+ fyn_anchor = fy_anchor_node(fya);
+
+ text = fy_anchor_get_text(fya, &len);
+ if (!text)
+ return NULL;
+
+ if (fyn_anchor == fyn) {
+ alloca_sprintf(&str, "*%.*s", (int)len, text);
+ } else {
+ fy_node_get_path_relative_to_alloca(fyn_anchor, fyn, &path);
+ alloca_sprintf(&str, "*%.*s/%s", (int)len, text, path);
+ }
+
+ path = strdup(str);
+ return path;
+}
+
+static struct fy_node *
+fy_document_load_node(struct fy_document *fyd, struct fy_parser *fyp,
+ struct fy_document_state **fydsp)
+{
+ struct fy_eventp *fyep = NULL;
+ struct fy_event *fye = NULL;
+ struct fy_node *fyn = NULL;
+ int rc, depth;
+ bool was_stream_start;
+
+ if (!fyd || !fyp)
+ return NULL;
+
+ /* only single documents */
+ fy_parser_set_next_single_document(fyp);
+ fy_parser_set_default_document_state(fyp, fyd->fyds);
+
+again:
+ was_stream_start = false;
+ do {
+ /* get next event */
+ fyep = fy_parse_private(fyp);
+
+ /* no more */
+ if (!fyep)
+ return NULL;
+
+ was_stream_start = fyep->e.type == FYET_STREAM_START;
+
+ if (was_stream_start) {
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyep = NULL;
+ }
+
+ } while (was_stream_start);
+
+ fye = &fyep->e;
+
+ /* STREAM_END */
+ if (fye->type == FYET_STREAM_END) {
+ fy_parse_eventp_recycle(fyp, fyep);
+
+ /* final STREAM_END? */
+ if (fyp->state == FYPS_END)
+ return NULL;
+
+ /* multi-stream */
+ goto again;
+ }
+
+ FYD_TOKEN_ERROR_CHECK(fyd, fy_event_get_token(fye), FYEM_DOC,
+ fye->type == FYET_DOCUMENT_START, err_out,
+ "bad event");
+
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyep = NULL;
+ fye = NULL;
+
+ fyd_doc_debug(fyd, "calling load_node() for root");
+ depth = 0;
+ rc = fy_parse_document_load_node(fyp, fyd, fy_parse_private(fyp), &fyn, &depth);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_parse_document_load_node() failed");
+
+ rc = fy_parse_document_load_end(fyp, fyd, fy_parse_private(fyp));
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_parse_document_load_node() failed");
+
+ /* always resolve parents */
+ fy_resolve_parent_node(fyd, fyn, NULL);
+
+ if (fydsp)
+ *fydsp = fy_document_state_ref(fyp->current_document_state);
+
+ return fyn;
+
+err_out:
+ fy_parse_eventp_recycle(fyp, fyep);
+ fyd->diag->on_error = false;
+ return NULL;
+}
+
+static struct fy_node *
+fy_node_build_internal(struct fy_document *fyd,
+ int (*parser_setup)(struct fy_parser *fyp, void *user),
+ void *user)
+{
+ struct fy_document_state *fyds = NULL;
+ struct fy_node *fyn = NULL;
+ struct fy_parser fyp_data, *fyp = &fyp_data;
+ struct fy_parse_cfg cfg;
+ struct fy_eventp *fyep;
+ int rc;
+ bool got_stream_end;
+
+ if (!fyd || !parser_setup)
+ return NULL;
+
+ cfg = fyd->parse_cfg;
+ cfg.diag = fyd->diag;
+ rc = fy_parse_setup(fyp, &cfg);
+ if (rc) {
+ fyd->diag->on_error = false;
+ return NULL;
+ }
+
+ rc = (*parser_setup)(fyp, user);
+ fyd_error_check(fyd, !rc, err_out,
+ "parser_setup() failed");
+
+ fyn = fy_document_load_node(fyd, fyp, &fyds);
+ fyd_error_check(fyd, fyn, err_out,
+ "fy_document_load_node() failed");
+
+ got_stream_end = false;
+ while (!got_stream_end && (fyep = fy_parse_private(fyp)) != NULL) {
+ if (fyep->e.type == FYET_STREAM_END)
+ got_stream_end = true;
+ fy_parse_eventp_recycle(fyp, fyep);
+ }
+
+ if (got_stream_end) {
+ fyep = fy_parse_private(fyp);
+
+ FYD_TOKEN_ERROR_CHECK(fyd, fy_event_get_token(&fyep->e), FYEM_DOC,
+ !fyep, err_out,
+ "trailing events after the last");
+
+ fy_parse_eventp_recycle(fyp, fyep);
+ }
+
+ rc = fy_document_state_merge(fyd->fyds, fyds);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_document_state_merge() failed");
+
+ fy_document_state_unref(fyds);
+
+ fy_parse_cleanup(fyp);
+
+ return fyn;
+
+err_out:
+ fy_node_detach_and_free(fyn);
+ fy_document_state_unref(fyds);
+ fy_parse_cleanup(fyp);
+ fyd->diag->on_error = false;
+ return NULL;
+}
+
+struct fy_node *fy_node_build_from_string(struct fy_document *fyd, const char *str, size_t len)
+{
+ struct fy_document_build_string_ctx ctx = {
+ .str = str,
+ .len = len,
+ };
+
+ return fy_node_build_internal(fyd, parser_setup_from_string, &ctx);
+}
+
+struct fy_node *fy_node_build_from_malloc_string(struct fy_document *fyd, char *str, size_t len)
+{
+ struct fy_document_build_malloc_string_ctx ctx = {
+ .str = str,
+ .len = len,
+ };
+
+ return fy_node_build_internal(fyd, parser_setup_from_malloc_string, &ctx);
+}
+
+struct fy_node *fy_node_build_from_file(struct fy_document *fyd, const char *file)
+{
+ struct fy_document_build_file_ctx ctx = {
+ .file = file,
+ };
+
+ return fy_node_build_internal(fyd, parser_setup_from_file, &ctx);
+}
+
+struct fy_node *fy_node_build_from_fp(struct fy_document *fyd, FILE *fp)
+{
+ struct fy_document_build_fp_ctx ctx = {
+ .name = NULL,
+ .fp = fp,
+ };
+
+ return fy_node_build_internal(fyd, parser_setup_from_fp, &ctx);
+}
+
+int fy_document_set_root(struct fy_document *fyd, struct fy_node *fyn)
+{
+ if (!fyd)
+ return -1;
+
+ if (fyn && fyn->attached)
+ return -1;
+
+ fy_node_detach_and_free(fyd->root);
+ fyd->root = NULL;
+
+ fyn->parent = NULL;
+ fyd->root = fyn;
+
+ if (fyn)
+ fyn->attached = true;
+
+ return 0;
+}
+
+#define FYNCSIF_ALIAS FY_BIT(0)
+#define FYNCSIF_SIMPLE FY_BIT(1)
+#define FYNCSIF_COPY FY_BIT(2)
+#define FYNCSIF_MALLOCED FY_BIT(3)
+
+static struct fy_node *
+fy_node_create_scalar_internal(struct fy_document *fyd, const char *data, size_t size,
+ unsigned int flags)
+{
+ const bool alias = !!(flags & FYNCSIF_ALIAS);
+ const bool simple = !!(flags & FYNCSIF_SIMPLE);
+ const bool copy = !!(flags & FYNCSIF_COPY);
+ const bool malloced = !!(flags & FYNCSIF_MALLOCED);
+ struct fy_node *fyn = NULL;
+ struct fy_input *fyi;
+ struct fy_atom handle;
+ enum fy_scalar_style style;
+ char *data_copy = NULL;
+
+ if (!fyd)
+ return NULL;
+
+ if (data && size == (size_t)-1)
+ size = strlen(data);
+
+ fyn = fy_node_alloc(fyd, FYNT_SCALAR);
+ fyd_error_check(fyd, fyn, err_out,
+ "fy_node_alloc() failed");
+
+ if (copy) {
+ data_copy = malloc(size);
+ fyd_error_check(fyd, data_copy, err_out,
+ "malloc() failed");
+ memcpy(data_copy, data, size);
+ fyi = fy_input_from_malloc_data(data_copy, size, &handle, simple);
+ } else if (malloced)
+ fyi = fy_input_from_malloc_data((void *)data, size, &handle, simple);
+ else
+ fyi = fy_input_from_data(data, size, &handle, simple);
+ fyd_error_check(fyd, fyi, err_out,
+ "fy_input_from_data() failed");
+ data_copy = NULL;
+
+ if (!alias) {
+ style = handle.style == FYAS_PLAIN ? FYSS_PLAIN : FYSS_DOUBLE_QUOTED;
+ fyn->scalar = fy_token_create(FYTT_SCALAR, &handle, style);
+ } else
+ fyn->scalar = fy_token_create(FYTT_ALIAS, &handle, NULL);
+
+ fyd_error_check(fyd, fyn->scalar, err_out,
+ "fy_token_create() failed");
+
+ fyn->style = !alias ? (style == FYSS_PLAIN ? FYNS_PLAIN : FYNS_DOUBLE_QUOTED) : FYNS_ALIAS;
+
+ /* take away the input reference */
+ fy_input_unref(fyi);
+
+ return fyn;
+
+err_out:
+ if (data_copy)
+ free(data_copy);
+ fy_node_detach_and_free(fyn);
+ fyd->diag->on_error = false;
+ return NULL;
+}
+
+struct fy_node *fy_node_create_scalar(struct fy_document *fyd, const char *data, size_t size)
+{
+ return fy_node_create_scalar_internal(fyd, data, size, 0);
+}
+
+struct fy_node *fy_node_create_alias(struct fy_document *fyd, const char *data, size_t size)
+{
+ return fy_node_create_scalar_internal(fyd, data, size, FYNCSIF_ALIAS);
+}
+
+struct fy_node *fy_node_create_scalar_copy(struct fy_document *fyd, const char *data, size_t size)
+{
+ return fy_node_create_scalar_internal(fyd, data, size, FYNCSIF_COPY);
+}
+
+struct fy_node *fy_node_create_alias_copy(struct fy_document *fyd, const char *data, size_t size)
+{
+ return fy_node_create_scalar_internal(fyd, data, size, FYNCSIF_ALIAS | FYNCSIF_COPY);
+}
+
+struct fy_node *fy_node_create_vscalarf(struct fy_document *fyd, const char *fmt, va_list ap)
+{
+ char *str;
+
+ if (!fyd || !fmt)
+ return NULL;
+
+ alloca_vsprintf(&str, fmt, ap);
+ return fy_node_create_scalar_internal(fyd, str, FY_NT, FYNCSIF_COPY);
+}
+
+struct fy_node *fy_node_create_scalarf(struct fy_document *fyd, const char *fmt, ...)
+{
+ va_list ap;
+ struct fy_node *fyn;
+
+ va_start(ap, fmt);
+ fyn = fy_node_create_vscalarf(fyd, fmt, ap);
+ va_end(ap);
+
+ return fyn;
+}
+
+int fy_node_set_tag(struct fy_node *fyn, const char *data, size_t len)
+{
+ struct fy_document *fyd;
+ struct fy_tag_scan_info info;
+ int handle_length, uri_length, prefix_length;
+ const char *handle_start;
+ int rc;
+ struct fy_atom handle;
+ struct fy_input *fyi = NULL;
+ struct fy_token *fyt = NULL, *fyt_td = NULL;
+
+ if (!fyn || !data || !len || !fyn->fyd)
+ return -1;
+
+ fyd = fyn->fyd;
+
+ if (len == (size_t)-1)
+ len = strlen(data);
+
+ memset(&info, 0, sizeof(info));
+
+ rc = fy_tag_scan(data, len, &info);
+ if (rc)
+ goto err_out;
+
+ handle_length = info.handle_length;
+ uri_length = info.uri_length;
+ prefix_length = info.prefix_length;
+
+ handle_start = data + prefix_length;
+
+ fyt_td = fy_document_state_lookup_tag_directive(fyd->fyds,
+ handle_start, handle_length);
+ if (!fyt_td)
+ goto err_out;
+
+ fyi = fy_input_from_data(data, len, &handle, true);
+ if (!fyi)
+ goto err_out;
+
+ handle.style = FYAS_URI;
+ handle.direct_output = false;
+ handle.storage_hint = 0;
+ handle.storage_hint_valid = false;
+
+ fyt = fy_token_create(FYTT_TAG, &handle, prefix_length,
+ handle_length, uri_length, fyt_td);
+ if (!fyt)
+ goto err_out;
+
+ fy_token_unref(fyn->tag);
+ fyn->tag = fyt;
+
+ /* take away the input reference */
+ fy_input_unref(fyi);
+
+ return 0;
+err_out:
+ fyd->diag->on_error = false;
+ return -1;
+}
+
+int fy_node_remove_tag(struct fy_node *fyn)
+{
+ if (!fyn || !fyn->tag)
+ return -1;
+
+ fy_token_unref(fyn->tag);
+ fyn->tag = NULL;
+
+ return 0;
+}
+
+struct fy_node *fy_node_create_sequence(struct fy_document *fyd)
+{
+ struct fy_node *fyn;
+
+ fyn = fy_node_alloc(fyd, FYNT_SEQUENCE);
+ if (!fyn)
+ return NULL;
+
+ return fyn;
+}
+
+struct fy_node *fy_node_create_mapping(struct fy_document *fyd)
+{
+ struct fy_node *fyn;
+
+ fyn = fy_node_alloc(fyd, FYNT_MAPPING);
+ if (!fyn)
+ return NULL;
+
+ return fyn;
+}
+
+static int fy_node_sequence_insert_prepare(struct fy_node *fyn_seq, struct fy_node *fyn)
+{
+ struct fy_document *fyd;
+
+ if (!fyn_seq || !fyn || fyn_seq->type != FYNT_SEQUENCE)
+ return -1;
+
+ /* can't insert a node that's attached already */
+ if (fyn->attached)
+ return -1;
+
+ /* a document must be associated with the sequence */
+ fyd = fyn_seq->fyd;
+ if (!fyd)
+ return -1;
+
+ /* the documents of the nodes must match */
+ if (fyn->fyd != fyd)
+ return -1;
+
+ fyn->parent = fyn_seq;
+
+ return 0;
+}
+
+int fy_node_sequence_append(struct fy_node *fyn_seq, struct fy_node *fyn)
+{
+ int ret;
+
+ ret = fy_node_sequence_insert_prepare(fyn_seq, fyn);
+ if (ret)
+ return ret;
+
+ fy_node_mark_synthetic(fyn_seq);
+ fy_node_list_add_tail(&fyn_seq->sequence, fyn);
+ fyn->attached = true;
+ return 0;
+}
+
+int fy_node_sequence_prepend(struct fy_node *fyn_seq, struct fy_node *fyn)
+{
+ int ret;
+
+ ret = fy_node_sequence_insert_prepare(fyn_seq, fyn);
+ if (ret)
+ return ret;
+
+ fy_node_mark_synthetic(fyn_seq);
+ fy_node_list_add(&fyn_seq->sequence, fyn);
+ fyn->attached = true;
+ return 0;
+}
+
+static bool fy_node_sequence_contains_node(struct fy_node *fyn_seq, struct fy_node *fyn)
+{
+ struct fy_node *fyni;
+
+ if (!fyn_seq || !fyn || fyn_seq->type != FYNT_SEQUENCE)
+ return false;
+
+ for (fyni = fy_node_list_head(&fyn_seq->sequence); fyni; fyni = fy_node_next(&fyn_seq->sequence, fyni))
+ if (fyni == fyn)
+ return true;
+
+ return false;
+}
+
+int fy_node_sequence_insert_before(struct fy_node *fyn_seq,
+ struct fy_node *fyn_mark, struct fy_node *fyn)
+{
+ int ret;
+
+ if (!fy_node_sequence_contains_node(fyn_seq, fyn_mark))
+ return -1;
+
+ ret = fy_node_sequence_insert_prepare(fyn_seq, fyn);
+ if (ret)
+ return ret;
+
+ fy_node_mark_synthetic(fyn_seq);
+ fy_node_list_insert_before(&fyn_seq->sequence, fyn_mark, fyn);
+ fyn->attached = true;
+
+ return 0;
+}
+
+int fy_node_sequence_insert_after(struct fy_node *fyn_seq,
+ struct fy_node *fyn_mark, struct fy_node *fyn)
+{
+ int ret;
+
+ if (!fy_node_sequence_contains_node(fyn_seq, fyn_mark))
+ return -1;
+
+ ret = fy_node_sequence_insert_prepare(fyn_seq, fyn);
+ if (ret)
+ return ret;
+
+ fy_node_mark_synthetic(fyn_seq);
+ fy_node_list_insert_after(&fyn_seq->sequence, fyn_mark, fyn);
+ fyn->attached = true;
+
+ return 0;
+}
+
+struct fy_node *fy_node_sequence_remove(struct fy_node *fyn_seq, struct fy_node *fyn)
+{
+ if (!fy_node_sequence_contains_node(fyn_seq, fyn))
+ return NULL;
+
+ fy_node_list_del(&fyn_seq->sequence, fyn);
+ fyn->parent = NULL;
+ fyn->attached = false;
+
+ fy_node_mark_synthetic(fyn_seq);
+
+ return fyn;
+}
+
+static struct fy_node_pair *
+fy_node_mapping_pair_insert_prepare(struct fy_node *fyn_map,
+ struct fy_node *fyn_key, struct fy_node *fyn_value)
+{
+ struct fy_document *fyd;
+ struct fy_node_pair *fynp;
+
+ if (!fyn_map || fyn_map->type != FYNT_MAPPING)
+ return NULL;
+
+ /* a document must be associated with the mapping */
+ fyd = fyn_map->fyd;
+ if (!fyd)
+ return NULL;
+
+ /* if not NULL, the documents of the nodes must match */
+ if ((fyn_key && fyn_key->fyd != fyd) ||
+ (fyn_value && fyn_value->fyd != fyd))
+ return NULL;
+
+ /* if not NULL neither the key nor the value must be attached */
+ if ((fyn_key && fyn_key->attached) ||
+ (fyn_value && fyn_value->attached))
+ return NULL;
+
+ /* if we don't allow duplicate keys */
+ if (!(fyd->parse_cfg.flags & FYPCF_ALLOW_DUPLICATE_KEYS)) {
+
+ if (fy_node_mapping_key_is_duplicate(fyn_map, fyn_key))
+ return NULL;
+ }
+
+ fynp = fy_node_pair_alloc(fyd);
+ if (!fynp)
+ return NULL;
+
+ if (fyn_key) {
+ fyn_key->parent = fyn_map;
+ fyn_key->key_root = true;
+ }
+ if (fyn_value)
+ fyn_value->parent = fyn_map;
+
+ fynp->key = fyn_key;
+ fynp->value = fyn_value;
+ fynp->parent = fyn_map;
+
+ return fynp;
+}
+
+int fy_node_mapping_append(struct fy_node *fyn_map,
+ struct fy_node *fyn_key, struct fy_node *fyn_value)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_pair_insert_prepare(fyn_map, fyn_key, fyn_value);
+ if (!fynp)
+ return -1;
+
+ fy_node_pair_list_add_tail(&fyn_map->mapping, fynp);
+ if (fyn_map->xl)
+ fy_accel_insert(fyn_map->xl , fyn_key, fynp);
+
+ if (fyn_key)
+ fyn_key->attached = true;
+ if (fyn_value)
+ fyn_value->attached = true;
+
+ fy_node_mark_synthetic(fyn_map);
+
+ return 0;
+}
+
+int fy_node_mapping_prepend(struct fy_node *fyn_map,
+ struct fy_node *fyn_key, struct fy_node *fyn_value)
+{
+ struct fy_node_pair *fynp;
+
+ fynp = fy_node_mapping_pair_insert_prepare(fyn_map, fyn_key, fyn_value);
+ if (!fynp)
+ return -1;
+
+ if (fyn_key)
+ fyn_key->attached = true;
+ if (fyn_value)
+ fyn_value->attached = true;
+ fy_node_pair_list_add(&fyn_map->mapping, fynp);
+ if (fyn_map->xl)
+ fy_accel_insert(fyn_map->xl, fyn_key, fynp);
+
+ fy_node_mark_synthetic(fyn_map);
+
+ return 0;
+}
+
+bool fy_node_mapping_contains_pair(struct fy_node *fyn_map, struct fy_node_pair *fynp)
+{
+ struct fy_node_pair *fynpi;
+
+ if (!fyn_map || !fynp || fyn_map->type != FYNT_MAPPING)
+ return false;
+
+ if (fyn_map->xl) {
+ fynpi = fy_node_accel_lookup_by_node(fyn_map, fynp->key);
+ if (fynpi == fynp)
+ return true;
+ } else {
+ for (fynpi = fy_node_pair_list_head(&fyn_map->mapping); fynpi; fynpi = fy_node_pair_next(&fyn_map->mapping, fynpi))
+ if (fynpi == fynp)
+ return true;
+ }
+
+ return false;
+}
+
+int fy_node_mapping_remove(struct fy_node *fyn_map, struct fy_node_pair *fynp)
+{
+ if (!fy_node_mapping_contains_pair(fyn_map, fynp))
+ return -1;
+
+ fy_node_pair_list_del(&fyn_map->mapping, fynp);
+ if (fyn_map->xl)
+ fy_accel_remove(fyn_map->xl, fynp->key);
+
+ if (fynp->key) {
+ fynp->key->parent = NULL;
+ fynp->key->attached = false;
+ }
+
+ if (fynp->value) {
+ fynp->value->parent = NULL;
+ fynp->value->attached = false;
+ }
+
+ fynp->parent = NULL;
+
+ return 0;
+}
+
+/* returns value */
+struct fy_node *fy_node_mapping_remove_by_key(struct fy_node *fyn_map, struct fy_node *fyn_key)
+{
+ struct fy_node_pair *fynp;
+ struct fy_node *fyn_value;
+
+ fynp = fy_node_mapping_lookup_pair(fyn_map, fyn_key);
+ if (!fynp)
+ return NULL;
+
+ fyn_value = fynp->value;
+ if (fyn_value) {
+ fyn_value->parent = NULL;
+ fyn_value->attached = false;
+ }
+
+ /* do not free the key if it's the same pointer */
+ if (fyn_key != fynp->key)
+ fy_node_detach_and_free(fyn_key);
+ fynp->value = NULL;
+
+ fy_node_pair_list_del(&fyn_map->mapping, fynp);
+ if (fyn_map->xl)
+ fy_accel_remove(fyn_map->xl, fynp->key);
+
+ fy_node_pair_detach_and_free(fynp);
+
+ fy_node_mark_synthetic(fyn_map);
+
+ return fyn_value;
+}
+
+void *fy_node_mapping_sort_ctx_arg(struct fy_node_mapping_sort_ctx *ctx)
+{
+ return ctx->arg;
+}
+
+static int fy_node_mapping_sort_cmp(
+#ifdef __APPLE__
+void *arg, const void *a, const void *b
+#else
+const void *a, const void *b, void *arg
+#endif
+)
+{
+ struct fy_node_mapping_sort_ctx *ctx = arg;
+ struct fy_node_pair * const *fynppa = a, * const *fynppb = b;
+
+ assert(fynppa >= ctx->fynpp && fynppa < ctx->fynpp + ctx->count);
+ assert(fynppb >= ctx->fynpp && fynppb < ctx->fynpp + ctx->count);
+
+ return ctx->key_cmp(*fynppa, *fynppb, ctx->arg);
+}
+
+/* not! thread safe! */
+#if !defined(HAVE_QSORT_R) || !HAVE_QSORT_R || defined(__EMSCRIPTEN__) || defined(_MSC_VER)
+static struct fy_node_mapping_sort_ctx *fy_node_mapping_sort_ctx_no_qsort_r;
+
+static int fy_node_mapping_sort_cmp_no_qsort_r(const void *a, const void *b)
+{
+#ifdef __APPLE__
+ return fy_node_mapping_sort_cmp(
+ fy_node_mapping_sort_ctx_no_qsort_r,
+ a, b);
+#else
+ return fy_node_mapping_sort_cmp( a, b,
+ fy_node_mapping_sort_ctx_no_qsort_r);
+#endif
+}
+
+#endif
+
+static int fy_node_scalar_cmp_default(struct fy_node *fyn_a,
+ struct fy_node *fyn_b,
+ void *arg)
+{
+ /* handles NULL cases */
+ if (fyn_a == fyn_b)
+ return 0;
+ if (!fyn_a)
+ return 1;
+ if (!fyn_b)
+ return -1;
+ return fy_token_cmp(fyn_a->scalar, fyn_b->scalar);
+}
+
+/* the default sort method */
+static int fy_node_mapping_sort_cmp_default(const struct fy_node_pair *fynp_a,
+ const struct fy_node_pair *fynp_b,
+ void *arg)
+{
+ int idx_a, idx_b;
+ bool alias_a, alias_b, scalar_a, scalar_b;
+ struct fy_node_cmp_arg *cmp_arg;
+ fy_node_scalar_compare_fn cmp_fn;
+ void *cmp_fn_arg;
+
+ cmp_arg = arg;
+ cmp_fn = cmp_arg ? cmp_arg->cmp_fn : fy_node_scalar_cmp_default;
+ cmp_fn_arg = cmp_arg ? cmp_arg->arg : NULL;
+
+ /* order is: maps first, followed by sequences, and last scalars sorted */
+ scalar_a = !fynp_a->key || fy_node_is_scalar(fynp_a->key);
+ scalar_b = !fynp_b->key || fy_node_is_scalar(fynp_b->key);
+
+ /* scalar? perform comparison */
+ if (scalar_a && scalar_b) {
+
+ /* if both are aliases, sort skipping the '*' */
+ alias_a = fy_node_is_alias(fynp_a->key);
+ alias_b = fy_node_is_alias(fynp_b->key);
+
+ /* aliases win */
+ if (alias_a && !alias_b)
+ return -1;
+
+ if (!alias_a && alias_b)
+ return 1;
+
+ return cmp_fn(fynp_a->key, fynp_b->key, cmp_fn_arg);
+ }
+
+ /* b is scalar, a is not */
+ if (!scalar_a && scalar_b)
+ return -1;
+
+ /* a is scalar, b is not */
+ if (scalar_a && !scalar_b)
+ return 1;
+
+ /* different types, mappings win */
+ if (fynp_a->key->type != fynp_b->key->type)
+ return fynp_a->key->type == FYNT_MAPPING ? -1 : 1;
+
+ /* ok, need to compare indices now */
+ idx_a = fy_node_mapping_get_pair_index(fynp_a->parent, fynp_a);
+ idx_b = fy_node_mapping_get_pair_index(fynp_b->parent, fynp_b);
+
+ return idx_a > idx_b ? 1 : (idx_a < idx_b ? -1 : 0);
+}
+
+void fy_node_mapping_fill_array(struct fy_node *fyn_map,
+ struct fy_node_pair **fynpp, int count)
+{
+ struct fy_node_pair *fynpi;
+ int i;
+
+ for (i = 0, fynpi = fy_node_pair_list_head(&fyn_map->mapping); i < count && fynpi;
+ fynpi = fy_node_pair_next(&fyn_map->mapping, fynpi), i++)
+ fynpp[i] = fynpi;
+
+ /* if there's enough space, put down a NULL at the end */
+ if (i < count)
+ fynpp[i++] = NULL;
+ assert(i == count);
+
+}
+
+void fy_node_mapping_perform_sort(struct fy_node *fyn_map,
+ fy_node_mapping_sort_fn key_cmp, void *arg,
+ struct fy_node_pair **fynpp, int count)
+{
+ struct fy_node_mapping_sort_ctx ctx;
+ struct fy_node_cmp_arg def_arg;
+
+ if (!key_cmp) {
+ def_arg.cmp_fn = fy_node_scalar_cmp_default;
+ def_arg.arg = arg;
+ } else {
+ def_arg.cmp_fn = NULL;
+ def_arg.arg = NULL;
+ }
+ ctx.key_cmp = key_cmp ? key_cmp : fy_node_mapping_sort_cmp_default;
+ ctx.arg = key_cmp ? arg : &def_arg;
+ ctx.fynpp = fynpp;
+ ctx.count = count;
+#if defined(HAVE_QSORT_R) && HAVE_QSORT_R && !defined(__EMSCRIPTEN__) && !defined(_MSC_VER)
+#ifdef __APPLE__
+ qsort_r(fynpp, count, sizeof(*fynpp), &ctx, fy_node_mapping_sort_cmp);
+#else
+ qsort_r(fynpp, count, sizeof(*fynpp), fy_node_mapping_sort_cmp, &ctx);
+#endif
+#else
+ /* caution, not thread safe */
+ fy_node_mapping_sort_ctx_no_qsort_r = &ctx;
+ qsort(fynpp, count, sizeof(*fynpp), fy_node_mapping_sort_cmp_no_qsort_r);
+ fy_node_mapping_sort_ctx_no_qsort_r = NULL;
+#endif
+}
+
+struct fy_node_pair **fy_node_mapping_sort_array(struct fy_node *fyn_map,
+ fy_node_mapping_sort_fn key_cmp, void *arg, int *countp)
+{
+ int count;
+ struct fy_node_pair **fynpp;
+
+ count = fy_node_mapping_item_count(fyn_map);
+ if (count < 0)
+ return NULL;
+
+ fynpp = malloc((count + 1) * sizeof(*fynpp));
+ if (!fynpp)
+ return NULL;
+
+ memset(fynpp, 0, (count + 1) * sizeof(*fynpp));
+
+ fy_node_mapping_fill_array(fyn_map, fynpp, count);
+ fy_node_mapping_perform_sort(fyn_map, key_cmp, arg, fynpp, count);
+
+ if (countp)
+ *countp = count;
+
+ return fynpp;
+}
+
+void fy_node_mapping_release_array(struct fy_node *fyn_map, struct fy_node_pair **fynpp)
+{
+ if (!fyn_map || !fynpp)
+ return;
+
+ free(fynpp);
+}
+
+int fy_node_mapping_sort(struct fy_node *fyn_map,
+ fy_node_mapping_sort_fn key_cmp,
+ void *arg)
+{
+ int count, i;
+ struct fy_node_pair **fynpp, *fynpi;
+
+ fynpp = fy_node_mapping_sort_array(fyn_map, key_cmp, arg, &count);
+ if (!fynpp)
+ return -1;
+
+ fy_node_pair_list_init(&fyn_map->mapping);
+ for (i = 0; i < count; i++) {
+ fynpi = fynpp[i];
+ fy_node_pair_list_add_tail(&fyn_map->mapping, fynpi);
+ }
+
+ fy_node_mapping_release_array(fyn_map, fynpp);
+
+ return 0;
+}
+
+int fy_node_sort(struct fy_node *fyn, fy_node_mapping_sort_fn key_cmp, void *arg)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi;
+ int ret;
+
+ if (!fyn)
+ return 0;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ fy_node_sort(fyni, key_cmp, arg);
+ }
+ break;
+
+ case FYNT_MAPPING:
+ ret = fy_node_mapping_sort(fyn, key_cmp, arg);
+ if (ret)
+ return ret;
+
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ /* the parent of the key is always NULL */
+ ret = fy_node_sort(fynp->key, key_cmp, arg);
+ if (ret)
+ return ret;
+
+ ret = fy_node_sort(fynp->value, key_cmp, arg);
+ if (ret)
+ return ret;
+
+ fynp->parent = fyn;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+struct fy_node *fy_node_vbuildf(struct fy_document *fyd, const char *fmt, va_list ap)
+{
+ struct fy_document_vbuildf_ctx vctx;
+ struct fy_node *fyn;
+
+ vctx.fmt = fmt;
+ va_copy(vctx.ap, ap);
+ fyn = fy_node_build_internal(fyd, parser_setup_from_fmt_ap, &vctx);
+ va_end(ap);
+
+ return fyn;
+}
+
+struct fy_node *fy_node_buildf(struct fy_document *fyd, const char *fmt, ...)
+{
+ struct fy_node *fyn;
+ va_list ap;
+
+ va_start(ap, fmt);
+ fyn = fy_node_vbuildf(fyd, fmt, ap);
+ va_end(ap);
+
+ return fyn;
+}
+
+struct fy_document *fy_document_vbuildf(const struct fy_parse_cfg *cfg, const char *fmt, va_list ap)
+{
+ struct fy_document *fyd;
+ struct fy_document_vbuildf_ctx vctx;
+
+ vctx.fmt = fmt;
+ va_copy(vctx.ap, ap);
+ fyd = fy_document_build_internal(cfg, parser_setup_from_fmt_ap, &vctx);
+ va_end(ap);
+
+ return fyd;
+}
+
+struct fy_document *fy_document_buildf(const struct fy_parse_cfg *cfg, const char *fmt, ...)
+{
+ struct fy_document *fyd;
+ va_list ap;
+
+ va_start(ap, fmt);
+ fyd = fy_document_vbuildf(cfg, fmt, ap);
+ va_end(ap);
+
+ return fyd;
+}
+
+struct flow_reader_container {
+ struct fy_reader reader;
+ const struct fy_parse_cfg *cfg;
+};
+
+static struct fy_diag *flow_reader_get_diag(struct fy_reader *fyr)
+{
+ struct flow_reader_container *frc = fy_container_of(fyr, struct flow_reader_container, reader);
+ return frc->cfg ? frc->cfg->diag : NULL;
+}
+
+static const struct fy_reader_ops reader_ops = {
+ .get_diag = flow_reader_get_diag,
+};
+
+struct fy_document *
+fy_flow_document_build_from_string(const struct fy_parse_cfg *cfg,
+ const char *str, size_t len, size_t *consumed)
+{
+ struct flow_reader_container frc;
+ struct fy_reader *fyr = NULL;
+ struct fy_parser fyp_data, *fyp = &fyp_data;
+ struct fy_parse_cfg cfg_data;
+ struct fy_input *fyi;
+ struct fy_document *fyd;
+ struct fy_mark mark;
+ int rc;
+
+ if (!str)
+ return NULL;
+
+ if (consumed)
+ *consumed = 0;
+
+ if (!cfg) {
+ memset(&cfg_data, 0, sizeof(cfg_data));
+ cfg_data.flags = FYPCF_DEFAULT_PARSE;
+ cfg = &cfg_data;
+ }
+
+ memset(&frc, 0, sizeof(frc));
+ fyr = &frc.reader;
+ frc.cfg = cfg;
+
+ fy_reader_setup(fyr, &reader_ops);
+
+ rc = fy_parse_setup(fyp, cfg);
+ if (rc)
+ goto err_no_parse;
+
+ fyi = fy_input_from_data(str, len, NULL, false);
+ if (!fyi)
+ goto err_no_input;
+
+ rc = fy_reader_input_open(fyr, fyi, NULL);
+ if (rc)
+ goto err_no_input_open;
+
+ fy_parser_set_reader(fyp, fyr);
+ fy_parser_set_flow_only_mode(fyp, true);
+
+ fyd = fy_parse_load_document(fyp);
+
+ fy_parse_cleanup(fyp);
+
+ if (fyd && consumed) {
+ fy_reader_get_mark(fyr, &mark);
+ *consumed = mark.input_pos;
+ }
+
+ fy_reader_cleanup(fyr);
+ fy_input_unref(fyi);
+
+ return fyd;
+
+err_no_input_open:
+ fy_input_unref(fyi);
+err_no_input:
+ fy_parse_cleanup(fyp);
+err_no_parse:
+ fy_reader_cleanup(fyr);
+ return NULL;
+}
+
+int fy_node_vscanf(struct fy_node *fyn, const char *fmt, va_list ap)
+{
+ size_t len;
+ char *fmt_cpy, *s, *e, *t, *te, *key, *fmtspec;
+ const char *value;
+ char *value0;
+ size_t value_len, value0_len;
+ int count, ret;
+ struct fy_node *fynv;
+ va_list apt;
+
+ if (!fyn || !fmt)
+ goto err_out;
+
+ len = strlen(fmt);
+ fmt_cpy = FY_ALLOCA(len + 1);
+ memcpy(fmt_cpy, fmt, len + 1);
+ s = fmt_cpy;
+ e = s + len;
+
+ /* the format is of the form 'access key' %fmt[...] */
+ /* so we search for a (non escaped '%) */
+ value0 = NULL;
+ value0_len = 0;
+ count = 0;
+ while (s < e) {
+ /* a '%' format must exist */
+ t = strchr(s, '%');
+ if (!t)
+ goto err_out;
+
+ /* skip escaped % */
+ if (t + 1 < e && t[1] == '%') {
+ s = t + 2;
+ continue;
+ }
+
+ /* trim spaces from key */
+ while (isspace(*s))
+ s++;
+ te = t;
+ while (te > s && isspace(te[-1]))
+ *--te = '\0';
+
+ key = s;
+
+ /* we have to scan until the next space that's not in char set */
+ fmtspec = t;
+ while (t < e) {
+ if (isspace(*t))
+ break;
+ /* character set (may include space) */
+ if (*t == '[') {
+ t++;
+ /* skip caret */
+ if (t < e && *t == '^')
+ t++;
+ /* if first character in the set is ']' accept it */
+ if (t < e && *t == ']')
+ t++;
+ /* now skip until end of character set */
+ while (t < e && *t != ']')
+ t++;
+ continue;
+ }
+ t++;
+ }
+ if (t < e)
+ *t++ = '\0';
+
+ /* find by (relative) path */
+ fynv = fy_node_by_path(fyn, key, t - s, FYNWF_DONT_FOLLOW);
+ if (!fynv || fynv->type != FYNT_SCALAR)
+ break;
+
+ /* there must be a text */
+ value = fy_token_get_text(fynv->scalar, &value_len);
+ if (!value)
+ break;
+
+ /* allocate buffer it's smaller than the one we have already */
+ if (!value0 || value0_len < value_len) {
+ value0 = FY_ALLOCA(value_len + 1);
+ value0_len = value_len;
+ }
+
+ memcpy(value0, value, value_len);
+ value0[value_len] = '\0';
+
+ va_copy(apt, ap);
+ /* scanf, all arguments are pointers */
+ (void)va_arg(ap, void *); /* advance argument pointer */
+
+ /* pass it to the system's scanf method */
+ ret = vsscanf(value0, fmtspec, apt);
+
+ /* since it's a single specifier, it must be one on success */
+ if (ret != 1)
+ break;
+
+ s = t;
+ count++;
+ }
+
+ return count;
+
+err_out:
+ errno = -EINVAL;
+ return -1;
+}
+
+int fy_node_scanf(struct fy_node *fyn, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = fy_node_vscanf(fyn, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+int fy_document_vscanf(struct fy_document *fyd, const char *fmt, va_list ap)
+{
+ return fy_node_vscanf(fyd->root, fmt, ap);
+}
+
+int fy_document_scanf(struct fy_document *fyd, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = fy_document_vscanf(fyd, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+bool fy_document_has_directives(const struct fy_document *fyd)
+{
+ struct fy_document_state *fyds;
+
+ if (!fyd)
+ return false;
+
+ fyds = fyd->fyds;
+ if (!fyds)
+ return false;
+
+ return fyds->fyt_vd || !fy_token_list_empty(&fyds->fyt_td);
+}
+
+bool fy_document_has_explicit_document_start(const struct fy_document *fyd)
+{
+ return fyd ? !fyd->fyds->start_implicit : false;
+}
+
+bool fy_document_has_explicit_document_end(const struct fy_document *fyd)
+{
+ return fyd ? !fyd->fyds->end_implicit : false;
+}
+
+void *fy_node_get_meta(struct fy_node *fyn)
+{
+ return fyn && fyn->has_meta ? fyn->meta : NULL;
+}
+
+int fy_node_set_meta(struct fy_node *fyn, void *meta)
+{
+ struct fy_document *fyd;
+
+ if (!fyn || !fyn->fyd)
+ return -1;
+
+ fyd = fyn->fyd;
+ if (fyn->has_meta && fyd->meta_clear_fn)
+ fyd->meta_clear_fn(fyn, fyn->meta, fyd->meta_user);
+ fyn->meta = meta;
+ fyn->has_meta = true;
+
+ return 0;
+}
+
+void fy_node_clear_meta(struct fy_node *fyn)
+{
+ struct fy_document *fyd;
+
+ if (!fyn || !fyn->has_meta || !fyn->fyd)
+ return;
+
+ fyd = fyn->fyd;
+ if (fyd->meta_clear_fn)
+ fyd->meta_clear_fn(fyn, fyn->meta, fyd->meta_user);
+ fyn->meta = NULL;
+ fyn->has_meta = false;
+}
+
+static void fy_node_clear_meta_internal(struct fy_node *fyn)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi;
+
+ if (!fyn)
+ return;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ fy_node_clear_meta_internal(fyni);
+ }
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ fy_node_clear_meta_internal(fynp->key);
+ fy_node_clear_meta_internal(fynp->value);
+ }
+ break;
+ }
+
+ fy_node_clear_meta(fyn);
+}
+
+int fy_document_register_meta(struct fy_document *fyd,
+ fy_node_meta_clear_fn clear_fn,
+ void *user)
+{
+ if (!fyd || !clear_fn || fyd->meta_clear_fn)
+ return -1;
+
+ fyd->meta_clear_fn = clear_fn;
+ fyd->meta_user = user;
+
+ return 0;
+}
+
+void fy_document_unregister_meta(struct fy_document *fyd)
+{
+ if (!fyd)
+ return;
+
+ fy_node_clear_meta_internal(fy_document_root(fyd));
+
+ fyd->meta_clear_fn = NULL;
+ fyd->meta_user = NULL;
+}
+
+int fy_document_set_userdata(struct fy_document *fyd, void *userdata)
+{
+ if (!fyd || !userdata)
+ return -1;
+
+ fyd->userdata = userdata;
+
+ return 0;
+}
+
+void* fy_document_get_userdata(struct fy_document *fyd)
+{
+ return fyd->userdata;
+}
+
+int fy_document_register_on_destroy(struct fy_document *fyd,
+ fy_document_on_destroy_fn on_destroy_fn)
+{
+ if (!fyd || !on_destroy_fn)
+ return -1;
+
+ fyd->on_destroy_fn = on_destroy_fn;
+
+ return 0;
+}
+
+void fy_document_unregister_on_destroy(struct fy_document *fyd)
+{
+ if (!fyd)
+ return;
+
+ fyd->on_destroy_fn = NULL;
+}
+
+bool fy_node_set_marker(struct fy_node *fyn, unsigned int marker)
+{
+ unsigned int prev_marks;
+
+ if (!fyn || marker > FYNWF_MAX_USER_MARKER)
+ return false;
+ prev_marks = fyn->marks;
+ fyn->marks |= FY_BIT(marker);
+ return !!(prev_marks & FY_BIT(marker));
+}
+
+bool fy_node_clear_marker(struct fy_node *fyn, unsigned int marker)
+{
+ unsigned int prev_marks;
+
+ if (!fyn || marker > FYNWF_MAX_USER_MARKER)
+ return false;
+ prev_marks = fyn->marks;
+ fyn->marks &= ~FY_BIT(marker);
+ return !!(prev_marks & FY_BIT(marker));
+}
+
+bool fy_node_is_marker_set(struct fy_node *fyn, unsigned int marker)
+{
+ if (!fyn || marker > FYNWF_MAX_USER_MARKER)
+ return false;
+ return !!(fyn->marks & FY_BIT(marker));
+}
+
+FILE *fy_document_get_error_fp(struct fy_document *fyd)
+{
+ /* just this for now */
+ return stderr;
+}
+
+enum fy_parse_cfg_flags fy_document_get_cfg_flags(const struct fy_document *fyd)
+{
+ if (!fyd)
+ return fy_parser_get_cfg_flags(NULL);
+
+ return fyd->parse_cfg.flags;
+}
+
+bool fy_document_can_be_accelerated(struct fy_document *fyd)
+{
+ if (!fyd)
+ return false;
+
+ return !(fyd->parse_cfg.flags & FYPCF_DISABLE_ACCELERATORS);
+}
+
+bool fy_document_is_accelerated(struct fy_document *fyd)
+{
+ if (!fyd)
+ return false;
+
+ return fyd->axl && fyd->naxl;
+}
+
+static int hd_anchor_hash(struct fy_accel *xl, const void *key, void *userdata, void *hash)
+{
+ struct fy_token *fyt = (void *)key;
+ unsigned int *hashp = hash;
+ const char *text;
+ size_t len;
+
+ text = fy_token_get_text(fyt, &len);
+ if (!text)
+ return -1;
+
+ *hashp = XXH32(text, len, 2654435761U);
+ return 0;
+}
+
+static bool hd_anchor_eq(struct fy_accel *xl, const void *hash, const void *key1, const void *key2, void *userdata)
+{
+ struct fy_token *fyt1 = (void *)key1, *fyt2 = (void *)key2;
+ const char *text1, *text2;
+ size_t len1, len2;
+
+ text1 = fy_token_get_text(fyt1, &len1);
+ if (!text1)
+ return false;
+ text2 = fy_token_get_text(fyt2, &len2);
+ if (!text2)
+ return false;
+
+ return len1 == len2 && !memcmp(text1, text2, len1);
+}
+
+static const struct fy_hash_desc hd_anchor = {
+ .size = sizeof(unsigned int),
+ .max_bucket_grow_limit = 6, /* TODO allow tuning */
+ .hash = hd_anchor_hash,
+ .eq = hd_anchor_eq,
+};
+
+static int hd_nanchor_hash(struct fy_accel *xl, const void *key, void *userdata, void *hash)
+{
+ struct fy_node *fyn = (void *)key;
+ unsigned int *hashp = hash;
+ uintptr_t ptr = (uintptr_t)fyn;
+
+ *hashp = XXH32(&ptr, sizeof(ptr), 2654435761U);
+
+ return 0;
+}
+
+static bool hd_nanchor_eq(struct fy_accel *xl, const void *hash, const void *key1, const void *key2, void *userdata)
+{
+ struct fy_node *fyn1 = (void *)key1, *fyn2 = (void *)key2;
+
+ return fyn1 == fyn2;
+}
+
+static const struct fy_hash_desc hd_nanchor = {
+ .size = sizeof(unsigned int),
+ .max_bucket_grow_limit = 6, /* TODO allow tuning */
+ .hash = hd_nanchor_hash,
+ .eq = hd_nanchor_eq,
+};
+
+
+static int hd_mapping_hash(struct fy_accel *xl, const void *key, void *userdata, void *hash)
+{
+ return fy_node_hash_uint((struct fy_node *)key, hash);
+}
+
+static bool hd_mapping_eq(struct fy_accel *xl, const void *hash, const void *key1, const void *key2, void *userdata)
+{
+ return fy_node_compare((struct fy_node *)key1, (struct fy_node *)key2);
+}
+
+static const struct fy_hash_desc hd_mapping = {
+ .size = sizeof(unsigned int),
+ .max_bucket_grow_limit = 6, /* TODO allow tuning */
+ .hash = hd_mapping_hash,
+ .eq = hd_mapping_eq,
+};
+
+typedef void (*fy_hash_update_fn)(void *state, const void *ptr, size_t size);
+
+static int
+fy_node_hash_internal(struct fy_node *fyn, fy_hash_update_fn update_fn, void *state)
+{
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp;
+ struct fy_node_pair **fynpp;
+ struct fy_token_iter iter;
+ int i, count, rc;
+ const struct fy_iter_chunk *ic;
+
+ if (!fyn) {
+ /* NULL */
+ update_fn(state, "s", 1); /* as zero length scalar */
+ return 0;
+ }
+
+ switch (fyn->type) {
+ case FYNT_SEQUENCE:
+ /* SEQUENCE */
+ update_fn(state, "S", 1);
+
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+
+ rc = fy_node_hash_internal(fyni, update_fn, state);
+ if (rc)
+ return rc;
+ }
+
+ break;
+
+ case FYNT_MAPPING:
+ count = fy_node_mapping_item_count(fyn);
+
+ fynpp = FY_ALLOCA(sizeof(*fynpp) * (count + 1));
+
+ fy_node_mapping_fill_array(fyn, fynpp, count);
+ fy_node_mapping_perform_sort(fyn, NULL, NULL, fynpp, count);
+
+ /* MAPPING */
+ update_fn(state, "M", 1);
+
+ for (i = 0; i < count; i++) {
+ fynp = fynpp[i];
+
+ /* MAPPING KEY */
+ update_fn(state, "K", 1);
+ rc = fy_node_hash_internal(fynp->key, update_fn, state);
+ if (rc)
+ return rc;
+
+ /* MAPPING VALUE */
+ update_fn(state, "V", 1);
+ rc = fy_node_hash_internal(fynp->value, update_fn, state);
+ if (rc)
+ return rc;
+ }
+
+ break;
+
+ case FYNT_SCALAR:
+ update_fn(state, !fy_node_is_alias(fyn) ? "s" : "A", 1);
+
+ fy_token_iter_start(fyn->scalar, &iter);
+ ic = NULL;
+ while ((ic = fy_token_iter_chunk_next(&iter, ic, &rc)) != NULL)
+ update_fn(state, ic->str, ic->len);
+ fy_token_iter_finish(&iter);
+
+ break;
+ }
+
+ return 0;
+}
+
+static void update_xx32(void *state, const void *ptr, size_t size)
+{
+ XXH32_update(state, ptr, size);
+}
+
+int fy_node_hash_uint(struct fy_node *fyn, unsigned int *hashp)
+{
+ XXH32_state_t state;
+ int rc;
+
+ XXH32_reset(&state, 2654435761U);
+
+ rc = fy_node_hash_internal(fyn, update_xx32, &state);
+ if (rc)
+ return rc;
+
+ *hashp = XXH32_digest(&state);
+ return 0;
+}
+
+struct fy_document_state *fy_document_get_document_state(struct fy_document *fyd)
+{
+ return fyd ? fyd->fyds : NULL;
+}
+
+int fy_document_set_document_state(struct fy_document *fyd, struct fy_document_state *fyds)
+{
+ /* document must exist and not have any contents */
+ if (!fyd || fyd->root)
+ return -1;
+
+ if (!fyds)
+ fyds = fy_document_state_default(NULL, NULL);
+ else
+ fyds = fy_document_state_ref(fyds);
+
+ if (!fyds)
+ return -1;
+
+ /* drop the previous document state */
+ fy_document_state_unref(fyd->fyds);
+ /* and use the new document state from now on */
+ fyd->fyds = fyds;
+
+ return 0;
+}
+
+struct fy_ptr_node *fy_ptr_node_create(struct fy_node *fyn)
+{
+ struct fy_ptr_node *fypn;
+
+ if (!fyn)
+ return NULL;
+
+ fypn = malloc(sizeof(*fypn));
+ if (!fypn)
+ return NULL;
+ memset(&fypn->node, 0, sizeof(fypn->node));
+ fypn->fyn = fyn;
+ return fypn;
+}
+
+void fy_ptr_node_destroy(struct fy_ptr_node *fypn)
+{
+ free(fypn);
+}
+
+void fy_ptr_node_list_free_all(struct fy_ptr_node_list *fypnl)
+{
+ struct fy_ptr_node *fypn;
+
+ while ((fypn = fy_ptr_node_list_pop(fypnl)) != NULL)
+ fy_ptr_node_destroy(fypn);
+}
+
+bool fy_ptr_node_list_contains(struct fy_ptr_node_list *fypnl, struct fy_node *fyn)
+{
+ struct fy_ptr_node *fypn;
+
+ if (!fypnl || !fyn)
+ return false;
+ for (fypn = fy_ptr_node_list_head(fypnl); fypn; fypn = fy_ptr_node_next(fypnl, fypn)) {
+ if (fypn->fyn == fyn)
+ return true;
+ }
+ return false;
+}
+
+struct fy_document *
+fy_document_create_from_event(struct fy_parser *fyp, struct fy_event *fye)
+{
+ struct fy_document *fyd;
+ int rc;
+
+ if (!fyp || !fye || fye->type != FYET_DOCUMENT_START)
+ return NULL;
+
+ /* TODO update document end */
+ fyd = fy_document_create(&fyp->cfg);
+ fyp_error_check(fyp, fyd, err_out,
+ "fy_document_create() failed");
+
+ rc = fy_document_set_document_state(fyd, fye->document_start.document_state);
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_document_set_document_state() failed");
+
+ return fyd;
+
+err_out:
+ fy_document_destroy(fyd);
+ return NULL;
+}
+
+int
+fy_document_update_from_event(struct fy_document *fyd, struct fy_parser *fyp, struct fy_event *fye)
+{
+ if (!fyd || !fyp || !fye || fye->type != FYET_DOCUMENT_END)
+ return -1;
+
+ /* nothing besides checks */
+ return 0;
+}
+
+struct fy_node *
+fy_node_create_from_event(struct fy_document *fyd, struct fy_parser *fyp, struct fy_event *fye)
+{
+ struct fy_node *fyn = NULL;
+ struct fy_token *value = NULL, *anchor = NULL;
+ int rc;
+
+ if (!fyd || !fye)
+ return NULL;
+
+ switch (fye->type) {
+ default:
+ break;
+
+ case FYET_SCALAR:
+ fyn = fy_node_alloc(fyd, FYNT_SCALAR);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_alloc() scalar failed");
+
+ value = fye->scalar.value;
+
+ if (value) /* NULL scalar */
+ fyn->style = fy_node_style_from_scalar_style(value->scalar.style);
+ else
+ fyn->style = FYNS_PLAIN;
+
+ /* NULLs are OK */
+ fyn->tag = fy_token_ref(fye->scalar.tag);
+ fyn->scalar = fy_token_ref(value);
+ anchor = fye->scalar.anchor;
+ break;
+
+ case FYET_ALIAS:
+ fyn = fy_node_alloc(fyd, FYNT_SCALAR);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_alloc() alias failed");
+
+ value = fye->alias.anchor;
+ fyn->style = FYNS_ALIAS;
+ fyn->scalar = fy_token_ref(value);
+ anchor = NULL;
+ break;
+
+ case FYET_MAPPING_START:
+ fyn = fy_node_create_mapping(fyd);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_create_mapping() failed");
+
+ value = fye->mapping_start.mapping_start;
+ fyn->style = value->type == FYTT_FLOW_MAPPING_START ? FYNS_FLOW : FYNS_BLOCK;
+
+ fyn->tag = fy_token_ref(fye->mapping_start.tag);
+ fyn->mapping_start = fy_token_ref(value);
+ fyn->mapping_end = NULL;
+ anchor = fye->mapping_start.anchor;
+ break;
+
+ case FYET_SEQUENCE_START:
+ fyn = fy_node_create_sequence(fyd);
+ fyp_error_check(fyp, fyn, err_out,
+ "fy_node_create_sequence() failed");
+
+ value = fye->sequence_start.sequence_start;
+
+ fyn->style = value->type == FYTT_FLOW_SEQUENCE_START ? FYNS_FLOW : FYNS_BLOCK;
+
+ fyn->tag = fy_token_ref(fye->sequence_start.tag);
+ fyn->sequence_start = fy_token_ref(value);
+ fyn->sequence_end = NULL;
+ anchor = fye->sequence_start.anchor;
+
+ break;
+
+ }
+
+ if (fyn && anchor) {
+ rc = fy_document_register_anchor(fyd, fyn, fy_token_ref(anchor));
+ fyp_error_check(fyp, !rc, err_out,
+ "fy_document_register_anchor() failed");
+ }
+
+ return fyn;
+
+err_out:
+ /* NULL OK */
+ fy_node_free(fyn);
+ return NULL;
+}
+
+int
+fy_node_update_from_event(struct fy_node *fyn, struct fy_parser *fyp, struct fy_event *fye)
+{
+ if (!fyn || !fyp || !fye)
+ return -1;
+
+ switch (fye->type) {
+
+ case FYET_MAPPING_END:
+ if (!fy_node_is_mapping(fyn))
+ return -1;
+ fy_token_unref(fyn->mapping_end);
+ fyn->mapping_end = fy_token_ref(fye->mapping_end.mapping_end);
+
+ break;
+
+ case FYET_SEQUENCE_END:
+ if (!fy_node_is_sequence(fyn))
+ return -1;
+ fy_token_unref(fyn->sequence_end);
+ fyn->sequence_end = fy_token_ref(fye->sequence_end.sequence_end);
+
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+struct fy_node_pair *
+fy_node_pair_create_with_key(struct fy_document *fyd, struct fy_node *fyn_parent, struct fy_node *fyn)
+{
+ struct fy_node_pair *fynp;
+ bool is_duplicate;
+
+ if (!fyd || !fyn_parent || !fy_node_is_mapping(fyn_parent))
+ return NULL;
+
+ /* if we don't allow duplicate keys */
+ if (!(fyd->parse_cfg.flags & FYPCF_ALLOW_DUPLICATE_KEYS)) {
+
+ /* make sure we don't add an already existing key */
+ is_duplicate = fy_node_mapping_key_is_duplicate(fyn_parent, fyn);
+ if (is_duplicate) {
+ FYD_NODE_ERROR(fyd, fyn, FYEM_DOC,
+ "duplicate mapping key");
+ return NULL;
+ }
+ }
+
+ fynp = fy_node_pair_alloc(fyd);
+ fyd_error_check(fyd, fynp, err_out,
+ "fy_node_pair_alloc() failed");
+
+ fynp->parent = fyn_parent;
+
+ fynp->key = fyn;
+ if (fynp->key)
+ fynp->key->attached = true;
+
+ return fynp;
+
+err_out:
+ fy_node_pair_free(fynp);
+ return NULL;
+
+}
+
+int
+fy_node_pair_update_with_value(struct fy_node_pair *fynp, struct fy_node *fyn)
+{
+ struct fy_node *fyn_parent;
+ int rc;
+
+ /* node pair must exist and value must be NULL */
+ if (!fynp || fynp->value || !fynp->parent || !fy_node_is_mapping(fynp->parent) || !fyn->fyd)
+ return -1;
+
+ fynp->value = fyn;
+ if (fynp->value)
+ fynp->value->attached = true;
+
+ fyn_parent = fynp->parent;
+
+ fy_node_pair_list_add_tail(&fyn_parent->mapping, fynp);
+ if (fyn_parent->xl) {
+ rc = fy_accel_insert(fyn_parent->xl, fynp->key, fynp);
+ fyd_error_check(fyn->fyd, !rc, err_out,
+ "fy_accel_insert() failed");
+ }
+
+ return 0;
+
+err_out:
+ fy_node_pair_list_del(&fyn_parent->mapping, fynp);
+ if (fyn)
+ fyn->attached = false;
+ fynp->value = NULL;
+ return -1;
+}
+
+int
+fy_node_sequence_add_item(struct fy_node *fyn_parent, struct fy_node *fyn)
+{
+ /* node pair must exist and value must be NULL */
+ if (!fyn_parent || !fyn || !fy_node_is_sequence(fyn_parent) || !fyn->fyd)
+ return -1;
+
+ fyn->parent = fyn_parent;
+ fy_node_list_add_tail(&fyn_parent->sequence, fyn);
+ fyn->attached = true;
+ return 0;
+}
+
+void fy_document_iterator_setup(struct fy_document_iterator *fydi)
+{
+ memset(fydi, 0, sizeof(*fydi));
+ fydi->state = FYDIS_WAITING_STREAM_START;
+ fydi->fyd = NULL;
+ fydi->iterate_root = NULL;
+
+ /* suppress recycling if we must */
+ fydi->suppress_recycling_force = getenv("FY_VALGRIND") && !getenv("FY_VALGRIND_RECYCLING");
+ fydi->suppress_recycling = fydi->suppress_recycling_force;
+
+ fy_eventp_list_init(&fydi->recycled_eventp);
+ fy_token_list_init(&fydi->recycled_token);
+
+ if (!fydi->suppress_recycling) {
+ fydi->recycled_eventp_list = &fydi->recycled_eventp;
+ fydi->recycled_token_list = &fydi->recycled_token;
+ } else {
+ fydi->recycled_eventp_list = NULL;
+ fydi->recycled_token_list = NULL;
+ }
+
+ /* start with the stack pointing to the in place data */
+ fydi->stack_top = (unsigned int)-1;
+ fydi->stack_alloc = sizeof(fydi->in_place) / sizeof(fydi->in_place[0]);
+ fydi->stack = fydi->in_place;
+}
+
+void fy_document_iterator_cleanup(struct fy_document_iterator *fydi)
+{
+ struct fy_token *fyt;
+ struct fy_eventp *fyep;
+
+ /* free the stack if it's not the inplace one */
+ if (fydi->stack != fydi->in_place)
+ free(fydi->stack);
+ fydi->stack_top = (unsigned int)-1;
+ fydi->stack_alloc = sizeof(fydi->in_place) / sizeof(fydi->in_place[0]);
+ fydi->stack = fydi->in_place;
+
+ while ((fyt = fy_token_list_pop(&fydi->recycled_token)) != NULL)
+ fy_token_free(fyt);
+
+ while ((fyep = fy_eventp_list_pop(&fydi->recycled_eventp)) != NULL)
+ fy_eventp_free(fyep);
+
+ fydi->state = FYDIS_WAITING_STREAM_START;
+ fydi->fyd = NULL;
+ fydi->iterate_root = NULL;
+}
+
+struct fy_document_iterator *fy_document_iterator_create(void)
+{
+ struct fy_document_iterator *fydi;
+
+ fydi = malloc(sizeof(*fydi));
+ if (!fydi)
+ return NULL;
+ fy_document_iterator_setup(fydi);
+ return fydi;
+}
+
+void fy_document_iterator_destroy(struct fy_document_iterator *fydi)
+{
+ if (!fydi)
+ return;
+ fy_document_iterator_cleanup(fydi);
+ free(fydi);
+}
+
+static struct fy_event *
+fydi_event_create(struct fy_document_iterator *fydi, struct fy_node *fyn, bool start)
+{
+ struct fy_eventp *fyep;
+ struct fy_event *fye;
+ struct fy_anchor *fya;
+ struct fy_token *anchor = NULL;
+
+ fyep = fy_document_iterator_eventp_alloc(fydi);
+ if (!fyep) {
+ fydi->state = FYDIS_ERROR;
+ return NULL;
+ }
+ fye = &fyep->e;
+
+ if (start) {
+ fya = fy_node_get_anchor(fyn);
+ anchor = fya ? fya->anchor : NULL;
+ }
+
+ switch (fyn->type) {
+
+ case FYNT_SCALAR:
+ if (fyn->style != FYNS_ALIAS) {
+ fye->type = FYET_SCALAR;
+ fye->scalar.anchor = fy_token_ref(anchor);
+ fye->scalar.tag = fy_token_ref(fyn->tag);
+ fye->scalar.value = fy_token_ref(fyn->scalar);
+ } else {
+ fye->type = FYET_ALIAS;
+ fye->alias.anchor = fy_token_ref(fyn->scalar);
+ }
+ break;
+
+ case FYNT_SEQUENCE:
+ if (start) {
+ fye->type = FYET_SEQUENCE_START;
+ fye->sequence_start.anchor = fy_token_ref(anchor);
+ fye->sequence_start.tag = fy_token_ref(fyn->tag);
+ fye->sequence_start.sequence_start = fy_token_ref(fyn->sequence_start);
+ } else {
+ fye->type = FYET_SEQUENCE_END;
+ fye->sequence_end.sequence_end = fy_token_ref(fyn->sequence_end);
+ }
+ break;
+
+ case FYNT_MAPPING:
+ if (start) {
+ fye->type = FYET_MAPPING_START;
+ fye->mapping_start.anchor = fy_token_ref(anchor);
+ fye->mapping_start.tag = fy_token_ref(fyn->tag);
+ fye->mapping_start.mapping_start = fy_token_ref(fyn->mapping_start);
+ } else {
+ fye->type = FYET_MAPPING_END;
+ fye->mapping_end.mapping_end = fy_token_ref(fyn->mapping_end);
+ }
+ break;
+ }
+
+ return fye;
+}
+
+struct fy_event *
+fy_document_iterator_stream_start(struct fy_document_iterator *fydi)
+{
+ struct fy_event *fye;
+
+ if (!fydi || fydi->state == FYDIS_ERROR)
+ return NULL;
+
+ /* both none and stream start are the same for this */
+ if (fydi->state != FYDIS_WAITING_STREAM_START &&
+ fydi->state != FYDIS_WAITING_STREAM_END_OR_DOCUMENT_START)
+ goto err_out;
+
+ fye = fy_document_iterator_event_create(fydi, FYET_STREAM_START);
+ if (!fye)
+ goto err_out;
+
+ fydi->state = FYDIS_WAITING_DOCUMENT_START;
+ return fye;
+
+err_out:
+ fydi->state = FYDIS_ERROR;
+ return NULL;
+}
+
+struct fy_event *
+fy_document_iterator_stream_end(struct fy_document_iterator *fydi)
+{
+ struct fy_event *fye;
+
+ if (!fydi || fydi->state == FYDIS_ERROR)
+ return NULL;
+
+ if (fydi->state != FYDIS_WAITING_STREAM_END_OR_DOCUMENT_START &&
+ fydi->state != FYDIS_WAITING_DOCUMENT_START)
+ goto err_out;
+
+ fye = fy_document_iterator_event_create(fydi, FYET_STREAM_END);
+ if (!fye)
+ goto err_out;
+
+ fydi->state = FYDIS_WAITING_STREAM_START;
+ return fye;
+
+err_out:
+ fydi->state = FYDIS_ERROR;
+ return NULL;
+}
+
+struct fy_event *
+fy_document_iterator_document_start(struct fy_document_iterator *fydi, struct fy_document *fyd)
+{
+ struct fy_event *fye = NULL;
+ struct fy_eventp *fyep;
+
+ if (!fydi || fydi->state == FYDIS_ERROR)
+ return NULL;
+
+ if (!fyd)
+ goto err_out;
+
+ /* we can transition to document start only from document start or stream end */
+ if (fydi->state != FYDIS_WAITING_DOCUMENT_START &&
+ fydi->state != FYDIS_WAITING_STREAM_END_OR_DOCUMENT_START)
+ goto err_out;
+
+ fyep = fy_document_iterator_eventp_alloc(fydi);
+ if (!fyep)
+ goto err_out;
+ fye = &fyep->e;
+
+ fydi->fyd = fyd;
+
+ /* the iteration root is the document root */
+ fydi->iterate_root = fyd->root;
+
+ /* suppress recycling if we must */
+ fydi->suppress_recycling = (fyd->parse_cfg.flags & FYPCF_DISABLE_RECYCLING) ||
+ fydi->suppress_recycling_force;
+
+ if (!fydi->suppress_recycling) {
+ fydi->recycled_eventp_list = &fydi->recycled_eventp;
+ fydi->recycled_token_list = &fydi->recycled_token;
+ } else {
+ fydi->recycled_eventp_list = NULL;
+ fydi->recycled_token_list = NULL;
+ }
+
+ fye->type = FYET_DOCUMENT_START;
+ fye->document_start.document_start = NULL;
+ fye->document_start.document_state = fy_document_state_ref(fyd->fyds);
+ fye->document_start.implicit = fyd->fyds->start_implicit;
+
+ /* and go into body */
+ fydi->state = FYDIS_WAITING_BODY_START_OR_DOCUMENT_END;
+
+ return fye;
+
+err_out:
+ fy_document_iterator_event_free(fydi, fye);
+ fydi->state = FYDIS_ERROR;
+ return NULL;
+}
+
+struct fy_event *
+fy_document_iterator_document_end(struct fy_document_iterator *fydi)
+{
+ struct fy_event *fye;
+
+ if (!fydi || fydi->state == FYDIS_ERROR)
+ return NULL;
+
+ if (!fydi->fyd || !fydi->fyd->fyds ||
+ fydi->state != FYDIS_WAITING_DOCUMENT_END)
+ goto err_out;
+
+ fye = fy_document_iterator_event_create(fydi, FYET_DOCUMENT_END, (int)fydi->fyd->fyds->end_implicit);
+ if (!fye)
+ goto err_out;
+
+ fydi->fyd = NULL;
+ fydi->iterate_root = NULL;
+
+ fydi->state = FYDIS_WAITING_STREAM_END_OR_DOCUMENT_START;
+ return fye;
+
+err_out:
+ fydi->state = FYDIS_ERROR;
+ return NULL;
+}
+
+static bool
+fy_document_iterator_ensure_space(struct fy_document_iterator *fydi, unsigned int space)
+{
+ struct fy_document_iterator_body_state *new_stack;
+ size_t new_size, copy_size;
+ unsigned int new_stack_alloc;
+
+ /* empty stack should always have enough space */
+ if (fydi->stack_top == (unsigned int)-1) {
+ assert(fydi->stack_alloc >= space);
+ return true;
+ }
+
+ if (fydi->stack_top + space < fydi->stack_alloc)
+ return true;
+
+ /* make sure we have enough space */
+ new_stack_alloc = fydi->stack_alloc * 2;
+ while (fydi->stack_top + space >= new_stack_alloc)
+ new_stack_alloc *= 2;
+
+ new_size = new_stack_alloc * sizeof(*new_stack);
+
+ if (fydi->stack == fydi->in_place) {
+ new_stack = malloc(new_size);
+ if (!new_stack)
+ return false;
+ copy_size = (fydi->stack_top + 1) * sizeof(*new_stack);
+ memcpy(new_stack, fydi->stack, copy_size);
+ } else {
+ new_stack = realloc(fydi->stack, new_size);
+ if (!new_stack)
+ return false;
+ }
+ fydi->stack = new_stack;
+ fydi->stack_alloc = new_stack_alloc;
+ return true;
+}
+
+static bool
+fydi_push_collection(struct fy_document_iterator *fydi, struct fy_node *fyn)
+{
+ struct fy_document_iterator_body_state *s;
+
+ /* make sure there's enough space */
+ if (!fy_document_iterator_ensure_space(fydi, 1))
+ return false;
+
+ /* get the next */
+ fydi->stack_top++;
+ s = &fydi->stack[fydi->stack_top];
+ s->fyn = fyn;
+
+ switch (fyn->type) {
+ case FYNT_SEQUENCE:
+ s->fyni = fy_node_list_head(&fyn->sequence);
+ break;
+
+ case FYNT_MAPPING:
+ s->fynp = fy_node_pair_list_head(&fyn->mapping);
+ s->processed_key = false;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ return true;
+}
+
+static inline void
+fydi_pop_collection(struct fy_document_iterator *fydi)
+{
+ assert(fydi->stack_top != (unsigned int)-1);
+ fydi->stack_top--;
+}
+
+static inline struct fy_document_iterator_body_state *
+fydi_last_collection(struct fy_document_iterator *fydi)
+{
+ if (fydi->stack_top == (unsigned int)-1)
+ return NULL;
+ return &fydi->stack[fydi->stack_top];
+}
+
+bool
+fy_document_iterator_body_next_internal(struct fy_document_iterator *fydi,
+ struct fy_document_iterator_body_result *res)
+{
+ struct fy_document_iterator_body_state *s;
+ struct fy_node *fyn, *fyn_col;
+ bool end;
+
+ if (!fydi || !res || fydi->state == FYDIS_ERROR)
+ return false;
+
+ if (fydi->state != FYDIS_WAITING_BODY_START_OR_DOCUMENT_END &&
+ fydi->state != FYDIS_BODY)
+ goto err_out;
+
+ end = false;
+ s = fydi_last_collection(fydi);
+ if (!s) {
+
+ fyn = fydi->iterate_root;
+ /* empty root, or last */
+ if (!fyn || fydi->state == FYDIS_BODY) {
+ fydi->state = FYDIS_WAITING_DOCUMENT_END;
+ return false;
+ }
+
+ /* ok, in body proper */
+ fydi->state = FYDIS_BODY;
+
+ } else {
+
+ fyn_col = s->fyn;
+ assert(fyn_col);
+
+ fyn = NULL;
+ if (fyn_col->type == FYNT_SEQUENCE) {
+ fyn = s->fyni;
+ if (fyn)
+ s->fyni = fy_node_next(&fyn_col->sequence, s->fyni);
+ } else {
+ assert(fyn_col->type == FYNT_MAPPING);
+ if (s->fynp) {
+ if (!s->processed_key) {
+ fyn = s->fynp->key;
+ s->processed_key = true;
+ } else {
+ fyn = s->fynp->value;
+ s->processed_key = false;
+
+ /* next in mapping after value */
+ s->fynp = fy_node_pair_next(&fyn_col->mapping, s->fynp);
+ }
+ }
+ }
+
+ /* if no next node in the collection, it's the end of the collection */
+ if (!fyn) {
+ fyn = fyn_col;
+ end = true;
+ }
+ }
+
+ assert(fyn);
+
+ /* only for collections */
+ if (fyn->type != FYNT_SCALAR) {
+ if (!end) {
+ /* push the new sequence */
+ if (!fydi_push_collection(fydi, fyn))
+ goto err_out;
+ } else
+ fydi_pop_collection(fydi);
+ }
+
+ res->fyn = fyn;
+ res->end = end;
+ return true;
+
+err_out:
+ fydi->state = FYDIS_ERROR;
+ return false;
+}
+
+struct fy_event *fy_document_iterator_body_next(struct fy_document_iterator *fydi)
+{
+ struct fy_document_iterator_body_result res;
+
+ if (!fydi)
+ return NULL;
+
+ if (!fy_document_iterator_body_next_internal(fydi, &res))
+ return NULL;
+
+ return fydi_event_create(fydi, res.fyn, !res.end);
+}
+
+void
+fy_document_iterator_node_start(struct fy_document_iterator *fydi, struct fy_node *fyn)
+{
+ /* do nothing on error */
+ if (!fydi || fydi->state == FYDIS_ERROR)
+ return;
+
+ /* and go into body */
+ fydi->state = FYDIS_WAITING_BODY_START_OR_DOCUMENT_END;
+ fydi->iterate_root = fyn;
+ fydi->fyd = NULL;
+}
+
+struct fy_node *fy_document_iterator_node_next(struct fy_document_iterator *fydi)
+{
+ struct fy_document_iterator_body_result res;
+
+ if (!fydi)
+ return NULL;
+
+ /* do not return ending nodes, are not interested in them */
+ do {
+ if (!fy_document_iterator_body_next_internal(fydi, &res))
+ return NULL;
+
+ } while (res.end);
+
+ return res.fyn;
+}
+
+bool fy_document_iterator_get_error(struct fy_document_iterator *fydi)
+{
+ if (!fydi)
+ return true;
+
+ if (fydi->state != FYDIS_ERROR)
+ return false;
+
+ fy_document_iterator_cleanup(fydi);
+
+ return true;
+}