/* * fy-docstate.c - YAML document state methods * * Copyright (c) 2019 Pantelis Antoniou * * SPDX-License-Identifier: MIT */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "fy-parse.h" #include "fy-doc.h" #include "fy-docstate.h" struct fy_document_state *fy_document_state_alloc(void) { struct fy_document_state *fyds; fyds = malloc(sizeof(*fyds)); if (!fyds) return NULL; memset(fyds, 0, sizeof(*fyds)); fyds->fyt_ds = NULL; fyds->fyt_vd = NULL; fy_token_list_init(&fyds->fyt_td); fyds->refs = 1; return fyds; } void fy_document_state_free(struct fy_document_state *fyds) { if (!fyds) return; assert(fyds->refs == 1); fy_token_unref(fyds->fyt_ds); fy_token_unref(fyds->fyt_vd); fy_token_list_unref_all(&fyds->fyt_td); free(fyds); } struct fy_document_state *fy_document_state_ref(struct fy_document_state *fyds) { if (!fyds) return NULL; assert(fyds->refs + 1 > 0); fyds->refs++; return fyds; } void fy_document_state_unref(struct fy_document_state *fyds) { if (!fyds) return; assert(fyds->refs > 0); if (fyds->refs == 1) fy_document_state_free(fyds); else fyds->refs--; } int fy_document_state_append_tag(struct fy_document_state *fyds, const char *handle, const char *prefix, bool is_default) { struct fy_token *fyt = NULL; struct fy_input *fyi = NULL; char *data; size_t size, handle_size, prefix_size; struct fy_atom atom; size = strlen(handle) + 1 + strlen(prefix); data = malloc(size + 1); if (!data) goto err_out; snprintf(data, size + 1, "%s %s", handle, prefix); fyi = fy_input_from_malloc_data(data, size, &atom, true); if (!fyi) goto err_out; data = NULL; /* ownership now at input */ handle_size = strlen(handle); prefix_size = strlen(prefix); fyt = fy_token_create(FYTT_TAG_DIRECTIVE, &atom, handle_size, prefix_size, is_default); if (!fyt) goto err_out; fy_token_list_add_tail(&fyds->fyt_td, fyt); if (!fy_tag_is_default_internal(handle, handle_size, prefix, prefix_size)) fyds->tags_explicit = true; /* take away the input reference */ fy_input_unref(fyi); return 0; err_out: fy_token_unref(fyt); fy_input_unref(fyi); if (data) free(data); return -1; } struct fy_document_state *fy_document_state_default( const struct fy_version *default_version, const struct fy_tag * const *default_tags) { struct fy_document_state *fyds = NULL; const struct fy_tag *fytag; int i, rc; if (!default_version) default_version = &fy_default_version; if (!default_tags) default_tags = fy_default_tags; fyds = fy_document_state_alloc(); if (!fyds) goto err_out; fyds->version = *default_version; fyds->version_explicit = false; fyds->tags_explicit = false; fyds->start_implicit = true; fyds->end_implicit = true; fyds->json_mode = false; memset(&fyds->start_mark, 0, sizeof(fyds->start_mark)); memset(&fyds->end_mark, 0, sizeof(fyds->end_mark)); fyds->fyt_ds = NULL; fyds->fyt_vd = NULL; fy_token_list_init(&fyds->fyt_td); for (i = 0; (fytag = default_tags[i]) != NULL; i++) { rc = fy_document_state_append_tag(fyds, fytag->handle, fytag->prefix, true); if (rc) goto err_out; } return fyds; err_out: fy_document_state_unref(fyds); return NULL; } struct fy_document_state *fy_document_state_copy(struct fy_document_state *fyds) { struct fy_document_state *fyds_new = NULL; struct fy_token *fyt_td, *fyt; fyds_new = fy_document_state_alloc(); if (!fyds_new) goto err_out; fyds_new->version = fyds->version; fyds_new->version_explicit = fyds->version_explicit; fyds_new->tags_explicit = fyds->tags_explicit; fyds_new->start_implicit = fyds->start_implicit; fyds_new->end_implicit = fyds->end_implicit; fyds_new->json_mode = fyds->json_mode; fyds_new->start_mark = fyds->start_mark; fyds_new->end_mark = fyds->end_mark; if (fyds->fyt_ds) { fyt = fy_token_alloc(); if (!fyt) goto err_out; fyt->type = FYTT_DOCUMENT_START; fyt->handle = fyds->fyt_ds->handle; /* take reference */ fy_input_ref(fyt->handle.fyi); fyt->handle.token_atom = true; fyds_new->fyt_ds = fyt; } if (fyds->fyt_vd) { fyt = fy_token_alloc(); if (!fyt) goto err_out; fyt->type = FYTT_VERSION_DIRECTIVE; fyt->handle = fyds->fyt_vd->handle; fyt->version_directive.vers = fyds->fyt_vd->version_directive.vers; /* take reference */ fy_input_ref(fyt->handle.fyi); fyt->handle.token_atom = true; fyds_new->fyt_vd = fyt; } for (fyt = fy_token_list_first(&fyds->fyt_td); fyt; fyt = fy_token_next(&fyds->fyt_td, fyt)) { fyt_td = fy_token_alloc(); if (!fyt_td) goto err_out; fyt_td->type = FYTT_TAG_DIRECTIVE; fyt_td->tag_directive.tag_length = fyt->tag_directive.tag_length; fyt_td->tag_directive.uri_length = fyt->tag_directive.uri_length; fyt_td->tag_directive.is_default = fyt->tag_directive.is_default; fyt_td->handle = fyt->handle; fyt_td->tag_directive.prefix0 = NULL; fyt_td->tag_directive.handle0 = NULL; /* take reference */ fy_input_ref(fyt_td->handle.fyi); fyt_td->handle.token_atom = true; /* append to the new document state */ fy_token_list_add_tail(&fyds_new->fyt_td, fyt_td); } return fyds_new; err_out: fy_document_state_unref(fyds_new); return NULL; } struct fy_token *fy_document_state_lookup_tag_directive(struct fy_document_state *fyds, const char *handle, size_t handle_size) { const char *td_handle; size_t td_handle_size; struct fy_token *fyt; if (!fyds) return NULL; for (fyt = fy_token_list_first(&fyds->fyt_td); fyt; fyt = fy_token_next(&fyds->fyt_td, fyt)) { td_handle = fy_tag_directive_token_handle(fyt, &td_handle_size); assert(td_handle); if (handle_size == td_handle_size && !memcmp(handle, td_handle, handle_size)) return fyt; } return NULL; } int fy_document_state_merge(struct fy_document_state *fyds, struct fy_document_state *fydsc) { const char *td_prefix, *tdc_handle, *tdc_prefix; size_t td_prefix_size, tdc_handle_size, tdc_prefix_size; struct fy_token *fyt, *fytc_td, *fyt_td; if (!fyds || !fydsc) return -1; /* check if there's a duplicate handle (which differs */ for (fytc_td = fy_token_list_first(&fydsc->fyt_td); fytc_td; fytc_td = fy_token_next(&fydsc->fyt_td, fytc_td)) { tdc_handle = fy_tag_directive_token_handle(fytc_td, &tdc_handle_size); if (!tdc_handle) goto err_out; tdc_prefix = fy_tag_directive_token_prefix(fytc_td, &tdc_prefix_size); if (!tdc_prefix) goto err_out; fyt_td = fy_document_state_lookup_tag_directive(fyds, tdc_handle, tdc_handle_size); if (fyt_td) { /* exists, must check whether the prefixes match */ td_prefix = fy_tag_directive_token_prefix(fyt_td, &td_prefix_size); assert(td_prefix); /* match? do nothing */ if (tdc_prefix_size == td_prefix_size && !memcmp(tdc_prefix, td_prefix, td_prefix_size)) continue; if (!fy_token_tag_directive_is_overridable(fyt_td)) goto err_out; /* override tag directive */ fy_token_list_del(&fyds->fyt_td, fyt_td); fy_token_unref(fyt_td); } fyt = fy_token_create(FYTT_TAG_DIRECTIVE, &fytc_td->handle, fytc_td->tag_directive.tag_length, fytc_td->tag_directive.uri_length, fytc_td->tag_directive.is_default); if (!fyt) goto err_out; fy_token_list_add_tail(&fyds->fyt_td, fyt); } /* merge other document state */ fyds->version_explicit |= fydsc->version_explicit; fyds->tags_explicit |= fydsc->tags_explicit; /* NOTE: json mode is not carried over */ if (fyds->version.major < fydsc->version.major || (fyds->version.major == fydsc->version.major && fyds->version.minor < fydsc->version.minor)) fyds->version = fydsc->version; return 0; err_out: return -1; } const struct fy_version * fy_document_state_version(struct fy_document_state *fyds) { /* return the default if not set */ return fyds ? &fyds->version : &fy_default_version; } const struct fy_mark *fy_document_state_start_mark(struct fy_document_state *fyds) { return fyds ? &fyds->start_mark : NULL; } const struct fy_mark *fy_document_state_end_mark(struct fy_document_state *fyds) { return fyds ? &fyds->end_mark : NULL; } bool fy_document_state_version_explicit(struct fy_document_state *fyds) { return fyds ? fyds->version_explicit : false; } bool fy_document_state_tags_explicit(struct fy_document_state *fyds) { return fyds ? fyds->tags_explicit : false; } bool fy_document_state_start_implicit(struct fy_document_state *fyds) { return fyds ? fyds->start_implicit : true; } bool fy_document_state_end_implicit(struct fy_document_state *fyds) { return fyds ? fyds->end_implicit : true; } bool fy_document_state_start_explicit(struct fy_document_state *fyds) { return fyds ? !fyds->start_implicit : false; } bool fy_document_state_end_explicit(struct fy_document_state *fyds) { return fyds ? !fyds->end_implicit : false; } bool fy_document_state_json_mode(struct fy_document_state *fyds) { return fyds ? fyds->json_mode : true; } const struct fy_tag * fy_document_state_tag_directive_iterate(struct fy_document_state *fyds, void **iterp) { struct fy_token *fyt; const struct fy_tag *tag; if (!fyds || !iterp) return NULL; fyt = *iterp; fyt = !fyt ? fy_token_list_head(&fyds->fyt_td) : fy_token_next(&fyds->fyt_td, fyt); if (!fyt) return NULL; /* sanity check */ assert(fyt->type == FYTT_TAG_DIRECTIVE); /* always refresh, should be relatively infrequent */ fyt->tag_directive.tag.handle = fy_tag_directive_token_handle0(fyt); fyt->tag_directive.tag.prefix = fy_tag_directive_token_prefix0(fyt); tag = &fyt->tag_directive.tag; *iterp = fyt; return tag; } struct fy_tag ** fy_document_state_tag_directives(struct fy_document_state *fyds) { struct fy_tag *tags, **tagsp; const struct fy_tag *fytag; size_t size, len; void *iter; char *s; int i; if (!fyds) return NULL; /* first find the extents of the area we're going to need */ i = 0; size = 0; iter = NULL; while ((fytag = fy_document_state_tag_directive_iterate(fyds, &iter)) != NULL) { size += strlen(fytag->handle) + 1 + strlen(fytag->prefix) + 1; i++; } size += sizeof(struct fy_tag) * i; size += sizeof(struct fy_tag *) * (i + 1); tagsp = malloc(size); if (!tagsp) return NULL; memset(tagsp, 0, size); tags = (void *)(tagsp + i + 1); s = (void *)(tags + i); i = 0; iter = NULL; while ((fytag = fy_document_state_tag_directive_iterate(fyds, &iter)) != NULL) { tags[i].handle = s; len = strlen(fytag->handle); memcpy(s, fytag->handle, len + 1); s += len + 1; tags[i].prefix = s; len = strlen(fytag->prefix); memcpy(s, fytag->prefix, len + 1); s += len + 1; tagsp[i] = &tags[i]; i++; } tagsp[i] = NULL; /* NULL terminated */ return tagsp; } int fy_document_state_shorten_tag(struct fy_document_state *fyds, const char *full_tag, size_t full_tag_size, const char **handlep, size_t *handle_sizep, const char **suffixp, size_t *suffix_sizep) { const char *td_handle, *td_prefix; size_t td_handle_size, td_prefix_size; struct fy_token *fyt, *fyt_best = NULL; size_t td_prefix_size_best = 0; if (!fyds || !full_tag || !handlep || !handle_sizep || !suffixp || !suffix_sizep) return -1; if (full_tag_size == FY_NT) full_tag_size = strlen(full_tag); for (fyt = fy_token_list_first(&fyds->fyt_td); fyt; fyt = fy_token_next(&fyds->fyt_td, fyt)) { td_prefix = fy_tag_directive_token_prefix(fyt, &td_prefix_size); if (!td_prefix) continue; if (td_prefix_size == 0 || td_prefix_size >= full_tag_size) continue; if (memcmp(td_prefix, full_tag, td_prefix_size)) continue; if (td_prefix_size_best >= td_prefix_size) continue; fyt_best = fyt; td_prefix_size_best = td_prefix_size; } if (!fyt_best) return -1; td_handle = fy_tag_directive_token_handle(fyt_best, &td_handle_size); if (!td_handle) return -1; *handlep = td_handle; *handle_sizep = td_handle_size; *suffixp = full_tag + td_prefix_size_best; *suffix_sizep = full_tag_size - td_prefix_size_best; return 0; }