// Copyright 2011-2023 David Robillard // SPDX-License-Identifier: ISC #include "serd/env.h" #include "env.h" #include "node.h" #include "serd/node.h" #include #include #include #include typedef struct { SerdNode* name; SerdNode* uri; } SerdPrefix; struct SerdEnvImpl { SerdPrefix* prefixes; size_t n_prefixes; SerdNode* base_uri_node; SerdURIView base_uri; }; SerdEnv* serd_env_new(const SerdStringView base_uri) { SerdEnv* env = (SerdEnv*)calloc(1, sizeof(struct SerdEnvImpl)); if (env && base_uri.length) { serd_env_set_base_uri(env, base_uri); } return env; } void serd_env_free(SerdEnv* const env) { if (!env) { return; } for (size_t i = 0; i < env->n_prefixes; ++i) { serd_node_free(env->prefixes[i].name); serd_node_free(env->prefixes[i].uri); } free(env->prefixes); serd_node_free(env->base_uri_node); free(env); } SerdURIView serd_env_base_uri_view(const SerdEnv* const env) { return env->base_uri; } const SerdNode* serd_env_base_uri(const SerdEnv* const env) { return env ? env->base_uri_node : NULL; } SerdStatus serd_env_set_base_uri(SerdEnv* const env, const SerdStringView uri) { if (!uri.length) { serd_node_free(env->base_uri_node); env->base_uri_node = NULL; env->base_uri = SERD_URI_NULL; return SERD_SUCCESS; } SerdNode* const old_base_uri = env->base_uri_node; // Resolve the new base against the current base in case it is relative const SerdURIView new_base_uri = serd_resolve_uri(serd_parse_uri(uri.data), env->base_uri); // Replace the current base URI env->base_uri_node = serd_new_parsed_uri(new_base_uri); env->base_uri = serd_node_uri_view(env->base_uri_node); serd_node_free(old_base_uri); return SERD_SUCCESS; } SERD_PURE_FUNC static SerdPrefix* serd_env_find(const SerdEnv* const env, const char* const name, const size_t name_len) { for (size_t i = 0; i < env->n_prefixes; ++i) { const SerdNode* const prefix_name = env->prefixes[i].name; if (prefix_name->length == name_len) { if (!memcmp(serd_node_string(prefix_name), name, name_len)) { return &env->prefixes[i]; } } } return NULL; } static void serd_env_add(SerdEnv* const env, const SerdStringView name, const SerdStringView uri) { SerdPrefix* const prefix = serd_env_find(env, name.data, name.length); if (prefix) { if (!!strcmp(serd_node_string(prefix->uri), uri.data)) { serd_node_free(prefix->uri); prefix->uri = serd_new_uri(uri); } } else { SerdPrefix* const new_prefixes = (SerdPrefix*)realloc( env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix)); if (new_prefixes) { env->prefixes = new_prefixes; env->prefixes[env->n_prefixes - 1].name = serd_new_string(name); env->prefixes[env->n_prefixes - 1].uri = serd_new_uri(uri); } } } SerdStatus serd_env_set_prefix(SerdEnv* const env, const SerdStringView name, const SerdStringView uri) { if (serd_uri_string_has_scheme(uri.data)) { // Set prefix to absolute URI serd_env_add(env, name, uri); return SERD_SUCCESS; } if (!env->base_uri_node) { return SERD_BAD_ARG; } // Resolve relative URI and create a new node and URI for it SerdNode* const abs_uri = serd_new_resolved_uri(uri, env->base_uri); // Set prefix to resolved (absolute) URI serd_env_add(env, name, serd_node_string_view(abs_uri)); serd_node_free(abs_uri); return SERD_SUCCESS; } bool serd_env_qualify(const SerdEnv* const env, const SerdNode* const uri, const SerdNode** const prefix, SerdStringView* const suffix) { if (!env) { return false; } for (size_t i = 0; i < env->n_prefixes; ++i) { const SerdNode* const prefix_uri = env->prefixes[i].uri; if (uri->length >= prefix_uri->length) { const char* prefix_str = serd_node_string(prefix_uri); const char* uri_str = serd_node_string(uri); if (!strncmp(uri_str, prefix_str, prefix_uri->length)) { *prefix = env->prefixes[i].name; suffix->data = uri_str + prefix_uri->length; suffix->length = uri->length - prefix_uri->length; return true; } } } return false; } SerdStatus serd_env_expand(const SerdEnv* const env, const SerdNode* const curie, SerdStringView* const uri_prefix, SerdStringView* const uri_suffix) { if (!env || !curie) { return SERD_BAD_CURIE; } const char* const str = serd_node_string(curie); const char* const colon = (const char*)memchr(str, ':', curie->length + 1); if (curie->type != SERD_CURIE || !colon) { return SERD_BAD_ARG; } const size_t name_len = (size_t)(colon - str); const SerdPrefix* const prefix = serd_env_find(env, str, name_len); if (prefix) { uri_prefix->data = serd_node_string(prefix->uri); uri_prefix->length = prefix->uri ? prefix->uri->length : 0; uri_suffix->data = colon + 1; uri_suffix->length = curie->length - name_len - 1; return SERD_SUCCESS; } return SERD_BAD_CURIE; } SerdNode* serd_env_expand_node(const SerdEnv* const env, const SerdNode* const node) { if (!env) { return NULL; } switch (node->type) { case SERD_LITERAL: break; case SERD_URI: return serd_new_resolved_uri(serd_node_string_view(node), env->base_uri); case SERD_CURIE: { SerdStringView prefix; SerdStringView suffix; if (serd_env_expand(env, node, &prefix, &suffix)) { return NULL; } const size_t len = prefix.length + suffix.length; SerdNode* ret = serd_node_malloc(len, 0, SERD_URI); char* buf = serd_node_buffer(ret); snprintf(buf, len + 1, "%s%s", prefix.data, suffix.data); ret->length = len; return ret; } case SERD_BLANK: break; } return NULL; } SerdStatus serd_env_write_prefixes(const SerdEnv* const env, const SerdSink* const sink) { SerdStatus st = SERD_SUCCESS; for (size_t i = 0; !st && i < env->n_prefixes; ++i) { const SerdPrefix* const prefix = &env->prefixes[i]; st = serd_sink_write_prefix(sink, prefix->name, prefix->uri); } return st; }