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