1 /*
2 Copyright 2011-2016 David Robillard <http://drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "serd_internal.h"
18
19 #include <stdlib.h>
20 #include <string.h>
21
22 typedef struct {
23 SerdNode name;
24 SerdNode uri;
25 } SerdPrefix;
26
27 struct SerdEnvImpl {
28 SerdPrefix* prefixes;
29 size_t n_prefixes;
30 SerdNode base_uri_node;
31 SerdURI base_uri;
32 };
33
34 SerdEnv*
serd_env_new(const SerdNode * base_uri)35 serd_env_new(const SerdNode* base_uri)
36 {
37 SerdEnv* env = (SerdEnv*)calloc(1, sizeof(struct SerdEnvImpl));
38 if (env && base_uri) {
39 serd_env_set_base_uri(env, base_uri);
40 }
41 return env;
42 }
43
44 void
serd_env_free(SerdEnv * env)45 serd_env_free(SerdEnv* env)
46 {
47 for (size_t i = 0; i < env->n_prefixes; ++i) {
48 serd_node_free(&env->prefixes[i].name);
49 serd_node_free(&env->prefixes[i].uri);
50 }
51 free(env->prefixes);
52 serd_node_free(&env->base_uri_node);
53 free(env);
54 }
55
56 const SerdNode*
serd_env_get_base_uri(const SerdEnv * env,SerdURI * out)57 serd_env_get_base_uri(const SerdEnv* env,
58 SerdURI* out)
59 {
60 if (out) {
61 *out = env->base_uri;
62 }
63 return &env->base_uri_node;
64 }
65
66 SerdStatus
serd_env_set_base_uri(SerdEnv * env,const SerdNode * uri)67 serd_env_set_base_uri(SerdEnv* env,
68 const SerdNode* uri)
69 {
70 if (!env || !uri) {
71 return SERD_ERR_BAD_ARG;
72 }
73
74 // Resolve base URI and create a new node and URI for it
75 SerdURI base_uri;
76 SerdNode base_uri_node = serd_node_new_uri_from_node(
77 uri, &env->base_uri, &base_uri);
78
79 if (base_uri_node.buf) {
80 // Replace the current base URI
81 serd_node_free(&env->base_uri_node);
82 env->base_uri_node = base_uri_node;
83 env->base_uri = base_uri;
84 return SERD_SUCCESS;
85 }
86 return SERD_ERR_BAD_ARG;
87 }
88
89 static inline SerdPrefix*
serd_env_find(const SerdEnv * env,const uint8_t * name,size_t name_len)90 serd_env_find(const SerdEnv* env,
91 const uint8_t* name,
92 size_t name_len)
93 {
94 for (size_t i = 0; i < env->n_prefixes; ++i) {
95 const SerdNode* const prefix_name = &env->prefixes[i].name;
96 if (prefix_name->n_bytes == name_len) {
97 if (!memcmp(prefix_name->buf, name, name_len)) {
98 return &env->prefixes[i];
99 }
100 }
101 }
102 return NULL;
103 }
104
105 static void
serd_env_add(SerdEnv * env,const SerdNode * name,const SerdNode * uri)106 serd_env_add(SerdEnv* env,
107 const SerdNode* name,
108 const SerdNode* uri)
109 {
110 SerdPrefix* const prefix = serd_env_find(env, name->buf, name->n_bytes);
111 if (prefix) {
112 SerdNode old_prefix_uri = prefix->uri;
113 prefix->uri = serd_node_copy(uri);
114 serd_node_free(&old_prefix_uri);
115 } else {
116 env->prefixes = (SerdPrefix*)realloc(
117 env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix));
118 env->prefixes[env->n_prefixes - 1].name = serd_node_copy(name);
119 env->prefixes[env->n_prefixes - 1].uri = serd_node_copy(uri);
120 }
121 }
122
123 SerdStatus
serd_env_set_prefix(SerdEnv * env,const SerdNode * name,const SerdNode * uri)124 serd_env_set_prefix(SerdEnv* env,
125 const SerdNode* name,
126 const SerdNode* uri)
127 {
128 if (!name->buf || uri->type != SERD_URI) {
129 return SERD_ERR_BAD_ARG;
130 } else if (serd_uri_string_has_scheme(uri->buf)) {
131 // Set prefix to absolute URI
132 serd_env_add(env, name, uri);
133 } else {
134 // Resolve relative URI and create a new node and URI for it
135 SerdURI abs_uri;
136 SerdNode abs_uri_node = serd_node_new_uri_from_node(
137 uri, &env->base_uri, &abs_uri);
138
139 // Set prefix to resolved (absolute) URI
140 serd_env_add(env, name, &abs_uri_node);
141 serd_node_free(&abs_uri_node);
142 }
143 return SERD_SUCCESS;
144 }
145
146 SerdStatus
serd_env_set_prefix_from_strings(SerdEnv * env,const uint8_t * name,const uint8_t * uri)147 serd_env_set_prefix_from_strings(SerdEnv* env,
148 const uint8_t* name,
149 const uint8_t* uri)
150 {
151 const SerdNode name_node = serd_node_from_string(SERD_LITERAL, name);
152 const SerdNode uri_node = serd_node_from_string(SERD_URI, uri);
153
154 return serd_env_set_prefix(env, &name_node, &uri_node);
155 }
156
157 bool
serd_env_qualify(const SerdEnv * env,const SerdNode * uri,SerdNode * prefix,SerdChunk * suffix)158 serd_env_qualify(const SerdEnv* env,
159 const SerdNode* uri,
160 SerdNode* prefix,
161 SerdChunk* suffix)
162 {
163 for (size_t i = 0; i < env->n_prefixes; ++i) {
164 const SerdNode* const prefix_uri = &env->prefixes[i].uri;
165 if (uri->n_bytes >= prefix_uri->n_bytes) {
166 if (!strncmp((const char*)uri->buf,
167 (const char*)prefix_uri->buf,
168 prefix_uri->n_bytes)) {
169 *prefix = env->prefixes[i].name;
170 suffix->buf = uri->buf + prefix_uri->n_bytes;
171 suffix->len = uri->n_bytes - prefix_uri->n_bytes;
172 return true;
173 }
174 }
175 }
176 return false;
177 }
178
179 SerdStatus
serd_env_expand(const SerdEnv * env,const SerdNode * curie,SerdChunk * uri_prefix,SerdChunk * uri_suffix)180 serd_env_expand(const SerdEnv* env,
181 const SerdNode* curie,
182 SerdChunk* uri_prefix,
183 SerdChunk* uri_suffix)
184 {
185 const uint8_t* const colon = (const uint8_t*)memchr(
186 curie->buf, ':', curie->n_bytes + 1);
187 if (curie->type != SERD_CURIE || !colon) {
188 return SERD_ERR_BAD_ARG;
189 }
190
191 const size_t name_len = colon - curie->buf;
192 const SerdPrefix* const prefix = serd_env_find(env, curie->buf, name_len);
193 if (prefix) {
194 uri_prefix->buf = prefix->uri.buf;
195 uri_prefix->len = prefix->uri.n_bytes;
196 uri_suffix->buf = colon + 1;
197 uri_suffix->len = curie->n_bytes - (colon - curie->buf) - 1;
198 return SERD_SUCCESS;
199 }
200 return SERD_ERR_BAD_CURIE;
201 }
202
203 SerdNode
serd_env_expand_node(const SerdEnv * env,const SerdNode * node)204 serd_env_expand_node(const SerdEnv* env,
205 const SerdNode* node)
206 {
207 switch (node->type) {
208 case SERD_CURIE: {
209 SerdChunk prefix;
210 SerdChunk suffix;
211 if (serd_env_expand(env, node, &prefix, &suffix)) {
212 return SERD_NODE_NULL;
213 }
214 const size_t len = prefix.len + suffix.len;
215 uint8_t* buf = (uint8_t*)malloc(len + 1);
216 SerdNode ret = { buf, len, 0, 0, SERD_URI };
217 snprintf((char*)buf, len + 1, "%s%s", prefix.buf, suffix.buf);
218 ret.n_chars = serd_strlen(buf, NULL, NULL);
219 return ret;
220 }
221 case SERD_URI: {
222 SerdURI ignored;
223 return serd_node_new_uri_from_node(node, &env->base_uri, &ignored);
224 }
225 default:
226 return SERD_NODE_NULL;
227 }
228 }
229
230 void
serd_env_foreach(const SerdEnv * env,SerdPrefixSink func,void * handle)231 serd_env_foreach(const SerdEnv* env,
232 SerdPrefixSink func,
233 void* handle)
234 {
235 for (size_t i = 0; i < env->n_prefixes; ++i) {
236 func(handle, &env->prefixes[i].name, &env->prefixes[i].uri);
237 }
238 }
239