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