1 /*
2   Copyright 2012-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 <assert.h>
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include "lv2/atom/forge.h"
24 #include "lv2/atom/util.h"
25 #include "lv2/midi/midi.h"
26 
27 #include "sratom/sratom.h"
28 
29 #define NS_RDF (const uint8_t*)"http://www.w3.org/1999/02/22-rdf-syntax-ns#"
30 #define NS_XSD (const uint8_t*)"http://www.w3.org/2001/XMLSchema#"
31 
32 #define USTR(str) ((const uint8_t*)(str))
33 
34 static const SerdStyle style = (SerdStyle)(
35 	SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED);
36 
37 typedef enum {
38 	MODE_SUBJECT,
39 	MODE_BODY,
40 	MODE_SEQUENCE
41 } ReadMode;
42 
43 struct SratomImpl {
44 	LV2_URID_Map*     map;
45 	LV2_Atom_Forge    forge;
46 	SerdEnv*          env;
47 	SerdNode          base_uri;
48 	SerdURI           base;
49 	SerdStatementSink write_statement;
50 	SerdEndSink       end_anon;
51 	void*             handle;
52 	LV2_URID          atom_Event;
53 	LV2_URID          atom_frameTime;
54 	LV2_URID          atom_beatTime;
55 	LV2_URID          midi_MidiEvent;
56 	unsigned          next_id;
57 	SratomObjectMode  object_mode;
58 	uint32_t          seq_unit;
59 	struct {
60 		SordNode* atom_childType;
61 		SordNode* atom_frameTime;
62 		SordNode* atom_beatTime;
63 		SordNode* rdf_first;
64 		SordNode* rdf_rest;
65 		SordNode* rdf_type;
66 		SordNode* rdf_value;
67 		SordNode* xsd_base64Binary;
68 	} nodes;
69 
70 	bool pretty_numbers;
71 };
72 
73 static void
74 read_node(Sratom*         sratom,
75           LV2_Atom_Forge* forge,
76           SordWorld*      world,
77           SordModel*      model,
78           const SordNode* node,
79           ReadMode        mode);
80 
81 Sratom*
sratom_new(LV2_URID_Map * map)82 sratom_new(LV2_URID_Map* map)
83 {
84 	Sratom* sratom = (Sratom*)calloc(1, sizeof(Sratom));
85 	if (sratom) {
86 		sratom->map            = map;
87 		sratom->atom_Event     = map->map(map->handle, LV2_ATOM__Event);
88 		sratom->atom_frameTime = map->map(map->handle, LV2_ATOM__frameTime);
89 		sratom->atom_beatTime  = map->map(map->handle, LV2_ATOM__beatTime);
90 		sratom->midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent);
91 		sratom->object_mode    = SRATOM_OBJECT_MODE_BLANK;
92 		lv2_atom_forge_init(&sratom->forge, map);
93 	}
94 	return sratom;
95 }
96 
97 void
sratom_free(Sratom * sratom)98 sratom_free(Sratom* sratom)
99 {
100 	if (sratom) {
101 		serd_node_free(&sratom->base_uri);
102 		free(sratom);
103 	}
104 }
105 
106 void
sratom_set_env(Sratom * sratom,SerdEnv * env)107 sratom_set_env(Sratom* sratom, SerdEnv* env)
108 {
109 	sratom->env = env;
110 }
111 
112 void
sratom_set_sink(Sratom * sratom,const char * base_uri,SerdStatementSink sink,SerdEndSink end_sink,void * handle)113 sratom_set_sink(Sratom*           sratom,
114                 const char*       base_uri,
115                 SerdStatementSink sink,
116                 SerdEndSink       end_sink,
117                 void*             handle)
118 {
119 	if (base_uri) {
120 		serd_node_free(&sratom->base_uri);
121 		sratom->base_uri = serd_node_new_uri_from_string(
122 			USTR(base_uri), NULL, NULL);
123 		serd_uri_parse(sratom->base_uri.buf, &sratom->base);
124 	}
125 	sratom->write_statement = sink;
126 	sratom->end_anon        = end_sink;
127 	sratom->handle          = handle;
128 }
129 
130 void
sratom_set_pretty_numbers(Sratom * sratom,bool pretty_numbers)131 sratom_set_pretty_numbers(Sratom* sratom,
132                           bool    pretty_numbers)
133 {
134 	sratom->pretty_numbers = pretty_numbers;
135 }
136 
137 void
sratom_set_object_mode(Sratom * sratom,SratomObjectMode object_mode)138 sratom_set_object_mode(Sratom*          sratom,
139                        SratomObjectMode object_mode)
140 {
141 	sratom->object_mode = object_mode;
142 }
143 
144 static void
gensym(SerdNode * out,char c,unsigned num)145 gensym(SerdNode* out, char c, unsigned num)
146 {
147 	out->n_bytes = out->n_chars = snprintf(
148 		(char*)out->buf, 10, "%c%u", c, num);
149 }
150 
151 static void
list_append(Sratom * sratom,LV2_URID_Unmap * unmap,unsigned * flags,SerdNode * s,SerdNode * p,SerdNode * node,uint32_t size,uint32_t type,const void * body)152 list_append(Sratom*           sratom,
153             LV2_URID_Unmap*   unmap,
154             unsigned*         flags,
155             SerdNode*         s,
156             SerdNode*         p,
157             SerdNode*         node,
158             uint32_t          size,
159             uint32_t          type,
160             const void*       body)
161 {
162 	// Generate a list node
163 	gensym(node, 'l', sratom->next_id);
164 	sratom->write_statement(sratom->handle, *flags, NULL,
165 	                        s, p, node, NULL, NULL);
166 
167 	// _:node rdf:first value
168 	*flags = SERD_LIST_CONT;
169 	*p = serd_node_from_string(SERD_URI, NS_RDF "first");
170 	sratom_write(sratom, unmap, *flags, node, p, type, size, body);
171 
172 	// Set subject to node and predicate to rdf:rest for next time
173 	gensym(node, 'l', ++sratom->next_id);
174 	*s = *node;
175 	*p = serd_node_from_string(SERD_URI, NS_RDF "rest");
176 }
177 
178 static void
list_end(SerdStatementSink sink,void * handle,const unsigned flags,SerdNode * s,SerdNode * p)179 list_end(SerdStatementSink sink,
180          void*             handle,
181          const unsigned    flags,
182          SerdNode*         s,
183          SerdNode*         p)
184 {
185 	// _:node rdf:rest rdf:nil
186 	const SerdNode nil = serd_node_from_string(SERD_URI, NS_RDF "nil");
187 	sink(handle, flags, NULL, s, p, &nil, NULL, NULL);
188 }
189 
190 static void
start_object(Sratom * sratom,uint32_t * flags,const SerdNode * subject,const SerdNode * predicate,const SerdNode * node,const char * type)191 start_object(Sratom*         sratom,
192              uint32_t*       flags,
193              const SerdNode* subject,
194              const SerdNode* predicate,
195              const SerdNode* node,
196              const char*     type)
197 {
198 	if (subject && predicate) {
199 		sratom->write_statement(sratom->handle, *flags|SERD_ANON_O_BEGIN, NULL,
200 		                        subject, predicate, node, NULL, NULL);
201 		// Start abbreviating object properties
202 		*flags |= SERD_ANON_CONT;
203 
204 		// Object is in a list, stop list abbreviating if necessary
205 		*flags &= ~SERD_LIST_CONT;
206 	}
207 	if (type) {
208 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "type");
209 		SerdNode o = serd_node_from_string(SERD_URI, USTR(type));
210 		sratom->write_statement(sratom->handle, *flags, NULL,
211 		                        node, &p, &o, NULL, NULL);
212 	}
213 }
214 
215 static bool
path_is_absolute(const char * path)216 path_is_absolute(const char* path)
217 {
218 	return (path[0] == '/'
219 	        || (isalpha(path[0]) && path[1] == ':'
220 	            && (path[2] == '/' || path[2] == '\\')));
221 }
222 
223 static SerdNode
number_type(const Sratom * sratom,const uint8_t * type)224 number_type(const Sratom* sratom, const uint8_t* type)
225 {
226 	if (sratom->pretty_numbers &&
227 	    (!strcmp((const char*)type, (const char*)NS_XSD "int") ||
228 	     !strcmp((const char*)type, (const char*)NS_XSD "long"))) {
229 		return serd_node_from_string(SERD_URI, NS_XSD "integer");
230 	} else if (sratom->pretty_numbers &&
231 	           (!strcmp((const char*)type, (const char*)NS_XSD "float") ||
232 	            !strcmp((const char*)type, (const char*)NS_XSD "double"))) {
233 		return serd_node_from_string(SERD_URI, NS_XSD "decimal");
234 	} else {
235 		return serd_node_from_string(SERD_URI, type);
236 	}
237 }
238 
239 int
sratom_write(Sratom * sratom,LV2_URID_Unmap * unmap,uint32_t flags,const SerdNode * subject,const SerdNode * predicate,uint32_t type_urid,uint32_t size,const void * body)240 sratom_write(Sratom*         sratom,
241              LV2_URID_Unmap* unmap,
242              uint32_t        flags,
243              const SerdNode* subject,
244              const SerdNode* predicate,
245              uint32_t        type_urid,
246              uint32_t        size,
247              const void*     body)
248 {
249 	const char* const type        = unmap->unmap(unmap->handle, type_urid);
250 	uint8_t           idbuf[12]   = "b0000000000";
251 	SerdNode          id          = serd_node_from_string(SERD_BLANK, idbuf);
252 	uint8_t           nodebuf[12] = "b0000000000";
253 	SerdNode          node        = serd_node_from_string(SERD_BLANK, nodebuf);
254 	SerdNode          object      = SERD_NODE_NULL;
255 	SerdNode          datatype    = SERD_NODE_NULL;
256 	SerdNode          language    = SERD_NODE_NULL;
257 	bool              new_node    = false;
258 	if (type_urid == 0 && size == 0) {
259 		object = serd_node_from_string(SERD_URI, USTR(NS_RDF "nil"));
260 	} else if (type_urid == sratom->forge.String) {
261 		object = serd_node_from_string(SERD_LITERAL, (const uint8_t*)body);
262 	} else if (type_urid == sratom->forge.Chunk) {
263 		datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
264 		object   = serd_node_new_blob(body, size, true);
265 		new_node = true;
266 	} else if (type_urid == sratom->forge.Literal) {
267 		const LV2_Atom_Literal_Body* lit = (const LV2_Atom_Literal_Body*)body;
268 		const uint8_t*         str = USTR(lit + 1);
269 		object = serd_node_from_string(SERD_LITERAL, str);
270 		if (lit->datatype) {
271 			datatype = serd_node_from_string(
272 				SERD_URI, USTR(unmap->unmap(unmap->handle, lit->datatype)));
273 		} else if (lit->lang) {
274 			const char*  lang       = unmap->unmap(unmap->handle, lit->lang);
275 			const char*  prefix     = "http://lexvo.org/id/iso639-3/";
276 			const size_t prefix_len = strlen(prefix);
277 			if (lang && !strncmp(lang, prefix, prefix_len)) {
278 				language = serd_node_from_string(
279 					SERD_LITERAL, USTR(lang + prefix_len));
280 			} else {
281 				fprintf(stderr, "Unknown language URID %d\n", lit->lang);
282 			}
283 		}
284 	} else if (type_urid == sratom->forge.URID) {
285 		const uint32_t urid = *(const uint32_t*)body;
286 		const uint8_t* str  = USTR(unmap->unmap(unmap->handle, urid));
287 		object = serd_node_from_string(SERD_URI, str);
288 	} else if (type_urid == sratom->forge.Path) {
289 		const uint8_t* str = USTR(body);
290 		if (path_is_absolute((const char*)str)) {
291 			new_node = true;
292 			object   = serd_node_new_file_uri(str, NULL, NULL, true);
293 		} else {
294 			if (!sratom->base_uri.buf ||
295 			    strncmp((const char*)sratom->base_uri.buf, "file://", 7)) {
296 				fprintf(stderr, "warning: Relative path but base is not a file URI.\n");
297 				fprintf(stderr, "warning: Writing ambiguous atom:Path literal.\n");
298 				object   = serd_node_from_string(SERD_LITERAL, str);
299 				datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path));
300 			} else {
301 				new_node = true;
302 				SerdNode rel = serd_node_new_file_uri(str, NULL, NULL, true);
303 				object = serd_node_new_uri_from_node(&rel, &sratom->base, NULL);
304 				serd_node_free(&rel);
305 			}
306 		}
307 	} else if (type_urid == sratom->forge.URI) {
308 		const uint8_t* str = USTR(body);
309 		object = serd_node_from_string(SERD_URI, str);
310 	} else if (type_urid == sratom->forge.Int) {
311 		new_node = true;
312 		object   = serd_node_new_integer(*(const int32_t*)body);
313 		datatype = number_type(sratom, NS_XSD "int");
314 	} else if (type_urid == sratom->forge.Long) {
315 		new_node = true;
316 		object   = serd_node_new_integer(*(const int64_t*)body);
317 		datatype = number_type(sratom, NS_XSD "long");
318 	} else if (type_urid == sratom->forge.Float) {
319 		new_node = true;
320 		object   = serd_node_new_decimal(*(const float*)body, 8);
321 		datatype = number_type(sratom, NS_XSD "float");
322 	} else if (type_urid == sratom->forge.Double) {
323 		new_node = true;
324 		object   = serd_node_new_decimal(*(const double*)body, 16);
325 		datatype = number_type(sratom, NS_XSD "double");
326 	} else if (type_urid == sratom->forge.Bool) {
327 		const int32_t val = *(const int32_t*)body;
328 		datatype = serd_node_from_string(SERD_URI, NS_XSD "boolean");
329 		object   = serd_node_from_string(SERD_LITERAL,
330 		                                 USTR(val ? "true" : "false"));
331 	} else if (type_urid == sratom->midi_MidiEvent) {
332 		new_node = true;
333 		datatype = serd_node_from_string(SERD_URI, USTR(LV2_MIDI__MidiEvent));
334 		uint8_t* str = (uint8_t*)calloc(size * 2 + 1, 1);
335 		for (uint32_t i = 0; i < size; ++i) {
336 			snprintf((char*)str + (2 * i), size * 2 + 1, "%02X",
337 			         (unsigned)*((const uint8_t*)body + i));
338 		}
339 		object = serd_node_from_string(SERD_LITERAL, USTR(str));
340 	} else if (type_urid == sratom->atom_Event) {
341 		const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body;
342 		gensym(&id, 'e', sratom->next_id++);
343 		start_object(sratom, &flags, subject, predicate, &id, NULL);
344 		SerdNode time;
345 		SerdNode p;
346 		if (sratom->seq_unit == sratom->atom_beatTime) {
347 			time     = serd_node_new_decimal(ev->time.beats, 16);
348 			p        = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__beatTime));
349 			datatype = number_type(sratom, NS_XSD "double");
350 		} else {
351 			time     = serd_node_new_integer(ev->time.frames);
352 			p        = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__frameTime));
353 			datatype = number_type(sratom, NS_XSD "long");
354 		}
355 		sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL,
356 		                        &id, &p, &time, &datatype, &language);
357 		serd_node_free(&time);
358 
359 		p = serd_node_from_string(SERD_URI, NS_RDF "value");
360 		sratom_write(sratom, unmap, SERD_ANON_CONT, &id, &p,
361 		             ev->body.type, ev->body.size, LV2_ATOM_BODY(&ev->body));
362 		if (sratom->end_anon) {
363 			sratom->end_anon(sratom->handle, &id);
364 		}
365 	} else if (type_urid == sratom->forge.Tuple) {
366 		gensym(&id, 't', sratom->next_id++);
367 		start_object(sratom, &flags, subject, predicate, &id, type);
368 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
369 		flags |= SERD_LIST_O_BEGIN;
370 		LV2_ATOM_TUPLE_BODY_FOREACH(body, size, i) {
371 			list_append(sratom, unmap, &flags, &id, &p, &node,
372 			            i->size, i->type, LV2_ATOM_BODY(i));
373 		}
374 		list_end(sratom->write_statement, sratom->handle, flags, &id, &p);
375 		if (sratom->end_anon) {
376 			sratom->end_anon(sratom->handle, &id);
377 		}
378 	} else if (type_urid == sratom->forge.Vector) {
379 		const LV2_Atom_Vector_Body* vec  = (const LV2_Atom_Vector_Body*)body;
380 		gensym(&id, 'v', sratom->next_id++);
381 		start_object(sratom, &flags, subject, predicate, &id, type);
382 		SerdNode p = serd_node_from_string(SERD_URI, (const uint8_t*)LV2_ATOM__childType);
383 		SerdNode child_type = serd_node_from_string(
384 			SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, vec->child_type));
385 		sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &child_type, NULL, NULL);
386 		p = serd_node_from_string(SERD_URI, NS_RDF "value");
387 		flags |= SERD_LIST_O_BEGIN;
388 		for (const char* i = (const char*)(vec + 1);
389 		     i < (const char*)vec + size;
390 		     i += vec->child_size) {
391 			list_append(sratom, unmap, &flags, &id, &p, &node,
392 			            vec->child_size, vec->child_type, i);
393 		}
394 		list_end(sratom->write_statement, sratom->handle, flags, &id, &p);
395 		if (sratom->end_anon) {
396 			sratom->end_anon(sratom->handle, &id);
397 		}
398 	} else if (lv2_atom_forge_is_object_type(&sratom->forge, type_urid)) {
399 		const LV2_Atom_Object_Body* obj   = (const LV2_Atom_Object_Body*)body;
400 		const char*                 otype = unmap->unmap(unmap->handle,
401 		                                                 obj->otype);
402 
403 		if (lv2_atom_forge_is_blank(&sratom->forge, type_urid, obj)) {
404 			gensym(&id, 'b', sratom->next_id++);
405 			start_object(sratom, &flags, subject, predicate, &id, otype);
406 		} else {
407 			id = serd_node_from_string(
408 				SERD_URI, (const uint8_t*)unmap->unmap(unmap->handle, obj->id));
409 			flags = 0;
410 			start_object(sratom, &flags, NULL, NULL, &id, otype);
411 		}
412 		LV2_ATOM_OBJECT_BODY_FOREACH(obj, size, prop) {
413 			const char* const key  = unmap->unmap(unmap->handle, prop->key);
414 			SerdNode          pred = serd_node_from_string(SERD_URI, USTR(key));
415 			sratom_write(sratom, unmap, flags, &id, &pred,
416 			             prop->value.type, prop->value.size,
417 			             LV2_ATOM_BODY(&prop->value));
418 		}
419 		if (sratom->end_anon && (flags & SERD_ANON_CONT)) {
420 			sratom->end_anon(sratom->handle, &id);
421 		}
422 	} else if (type_urid == sratom->forge.Sequence) {
423 		const LV2_Atom_Sequence_Body* seq = (const LV2_Atom_Sequence_Body*)body;
424 		gensym(&id, 'v', sratom->next_id++);
425 		start_object(sratom, &flags, subject, predicate, &id, type);
426 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
427 		flags |= SERD_LIST_O_BEGIN;
428 		LV2_ATOM_SEQUENCE_BODY_FOREACH(seq, size, ev) {
429 			sratom->seq_unit = seq->unit;
430 			list_append(sratom, unmap, &flags, &id, &p, &node,
431 			            sizeof(LV2_Atom_Event) + ev->body.size,
432 			            sratom->atom_Event,
433 			            ev);
434 		}
435 		list_end(sratom->write_statement, sratom->handle, flags, &id, &p);
436 		if (sratom->end_anon && subject && predicate) {
437 			sratom->end_anon(sratom->handle, &id);
438 		}
439 	} else {
440 		gensym(&id, 'b', sratom->next_id++);
441 		start_object(sratom, &flags, subject, predicate, &id, type);
442 		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
443 		SerdNode o = serd_node_new_blob(body, size, true);
444 		datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
445 		sratom->write_statement(sratom->handle, flags, NULL, &id, &p, &o, &datatype, NULL);
446 		if (sratom->end_anon && subject && predicate) {
447 			sratom->end_anon(sratom->handle, &id);
448 		}
449 		serd_node_free(&o);
450 	}
451 
452 	if (object.buf) {
453 		SerdNode def_s = serd_node_from_string(SERD_BLANK, USTR("atom"));
454 		SerdNode def_p = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
455 		if (!subject) {
456 			subject = &def_s;
457 		}
458 		if (!predicate) {
459 			predicate = &def_p;
460 		}
461 		sratom->write_statement(sratom->handle, flags, NULL,
462 		                        subject, predicate, &object, &datatype, &language);
463 	}
464 
465 	if (new_node) {
466 		serd_node_free(&object);
467 	}
468 
469 	return 0;
470 }
471 
472 char*
sratom_to_turtle(Sratom * sratom,LV2_URID_Unmap * unmap,const char * base_uri,const SerdNode * subject,const SerdNode * predicate,uint32_t type,uint32_t size,const void * body)473 sratom_to_turtle(Sratom*         sratom,
474                  LV2_URID_Unmap* unmap,
475                  const char*     base_uri,
476                  const SerdNode* subject,
477                  const SerdNode* predicate,
478                  uint32_t        type,
479                  uint32_t        size,
480                  const void*     body)
481 {
482 	SerdURI     buri   = SERD_URI_NULL;
483 	SerdNode    base   = serd_node_new_uri_from_string(USTR(base_uri), &sratom->base, &buri);
484 	SerdEnv*    env    = sratom->env ? sratom->env : serd_env_new(NULL);
485 	SerdChunk   str    = { NULL, 0 };
486 	SerdWriter* writer = serd_writer_new(
487 		SERD_TURTLE, style, env, &buri, serd_chunk_sink, &str);
488 
489 	serd_env_set_base_uri(env, &base);
490 	sratom_set_sink(sratom, base_uri,
491 	                (SerdStatementSink)serd_writer_write_statement,
492 	                (SerdEndSink)serd_writer_end_anon,
493 	                writer);
494 	sratom_write(sratom, unmap, SERD_EMPTY_S,
495 	             subject, predicate, type, size, body);
496 	serd_writer_finish(writer);
497 
498 	serd_writer_free(writer);
499 	if (!sratom->env) {
500 		serd_env_free(env);
501 	}
502 	serd_node_free(&base);
503 	return (char*)serd_chunk_sink_finish(&str);
504 }
505 
506 static void
read_list_value(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,ReadMode mode)507 read_list_value(Sratom*         sratom,
508                 LV2_Atom_Forge* forge,
509                 SordWorld*      world,
510                 SordModel*      model,
511                 const SordNode* node,
512                 ReadMode        mode)
513 {
514 	SordNode* fst = sord_get(model, node, sratom->nodes.rdf_first, NULL, NULL);
515 	SordNode* rst = sord_get(model, node, sratom->nodes.rdf_rest, NULL, NULL);
516 	if (fst && rst) {
517 		read_node(sratom, forge, world, model, fst, mode);
518 		read_list_value(sratom, forge, world, model, rst, mode);
519 	}
520 	sord_node_free(world, rst);
521 	sord_node_free(world, fst);
522 }
523 
524 static void
read_resource(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,LV2_URID otype)525 read_resource(Sratom*         sratom,
526               LV2_Atom_Forge* forge,
527               SordWorld*      world,
528               SordModel*      model,
529               const SordNode* node,
530               LV2_URID        otype)
531 {
532 	LV2_URID_Map* map = sratom->map;
533 	SordQuad      q   = { node, NULL, NULL, NULL };
534 	SordIter*     i   = sord_find(model, q);
535 	SordQuad      match;
536 	for (; !sord_iter_end(i); sord_iter_next(i)) {
537 		sord_iter_get(i, match);
538 		const SordNode* p      = match[SORD_PREDICATE];
539 		const SordNode* o      = match[SORD_OBJECT];
540 		const char*     p_uri  = (const char*)sord_node_get_string(p);
541 		uint32_t        p_urid = map->map(map->handle, p_uri);
542 		if (!(sord_node_equals(p, sratom->nodes.rdf_type) &&
543 		      sord_node_get_type(o) == SORD_URI &&
544 		      map->map(map->handle, (const char*)sord_node_get_string(o)) == otype)) {
545 			lv2_atom_forge_key(forge, p_urid);
546 			read_node(sratom, forge, world, model, o, MODE_BODY);
547 		}
548 	}
549 	sord_iter_free(i);
550 }
551 
552 static uint32_t
atom_size(Sratom * sratom,uint32_t type_urid)553 atom_size(Sratom* sratom, uint32_t type_urid)
554 {
555 	if (type_urid == sratom->forge.Int) {
556 		return sizeof(int32_t);
557 	} else if (type_urid == sratom->forge.Long) {
558 		return sizeof(int64_t);
559 	} else if (type_urid == sratom->forge.Float) {
560 		return sizeof(float);
561 	} else if (type_urid == sratom->forge.Double) {
562 		return sizeof(double);
563 	} else if (type_urid == sratom->forge.Bool) {
564 		return sizeof(int32_t);
565 	} else if (type_urid == sratom->forge.URID) {
566 		return sizeof(uint32_t);
567 	}
568 	return 0;
569 }
570 
571 static void
read_literal(Sratom * sratom,LV2_Atom_Forge * forge,const SordNode * node)572 read_literal(Sratom* sratom, LV2_Atom_Forge* forge, const SordNode* node)
573 {
574 	assert(sord_node_get_type(node) == SORD_LITERAL);
575 
576 	size_t        len = 0;
577 	const char*   str = (const char*)sord_node_get_string_counted(node, &len);
578 	SordNode*   datatype = sord_node_get_datatype(node);
579 	const char* language = sord_node_get_language(node);
580 	if (datatype) {
581 		const char* type_uri = (const char*)sord_node_get_string(datatype);
582 		if (!strcmp(type_uri, (const char*)NS_XSD "int") ||
583 		    !strcmp(type_uri, (const char*)NS_XSD "integer")) {
584 			lv2_atom_forge_int(forge, strtol(str, NULL, 10));
585 		} else if (!strcmp(type_uri, (const char*)NS_XSD "long")) {
586 			lv2_atom_forge_long(forge, strtol(str, NULL, 10));
587 		} else if (!strcmp(type_uri, (const char*)NS_XSD "float") ||
588 		           !strcmp(type_uri, (const char*)NS_XSD "decimal")) {
589 			lv2_atom_forge_float(forge, serd_strtod(str, NULL));
590 		} else if (!strcmp(type_uri, (const char*)NS_XSD "double")) {
591 			lv2_atom_forge_double(forge, serd_strtod(str, NULL));
592 		} else if (!strcmp(type_uri, (const char*)NS_XSD "boolean")) {
593 			lv2_atom_forge_bool(forge, !strcmp(str, "true"));
594 		} else if (!strcmp(type_uri, (const char*)NS_XSD "base64Binary")) {
595 			size_t size = 0;
596 			void*  body = serd_base64_decode(USTR(str), len, &size);
597 			lv2_atom_forge_atom(forge, size, forge->Chunk);
598 			lv2_atom_forge_write(forge, body, size);
599 			free(body);
600 		} else if (!strcmp(type_uri, LV2_ATOM__Path)) {
601 			lv2_atom_forge_path(forge, str, len);
602 		} else if (!strcmp(type_uri, LV2_MIDI__MidiEvent)) {
603 			lv2_atom_forge_atom(forge, len / 2, sratom->midi_MidiEvent);
604 			for (const char* s = str; s < str + len; s += 2) {
605 				unsigned num;
606 				sscanf(s, "%2X", &num);
607 				const uint8_t c = num;
608 				lv2_atom_forge_raw(forge, &c, 1);
609 			}
610 			lv2_atom_forge_pad(forge, len / 2);
611 		} else {
612 			lv2_atom_forge_literal(
613 				forge, str, len,
614 				sratom->map->map(sratom->map->handle, type_uri),
615 				0);
616 		}
617 	} else if (language) {
618 		const char*  prefix   = "http://lexvo.org/id/iso639-3/";
619 		const size_t lang_len = strlen(prefix) + strlen(language);
620 		char*        lang_uri = (char*)calloc(lang_len + 1, 1);
621 		snprintf(lang_uri, lang_len + 1, "%s%s", prefix, language);
622 		lv2_atom_forge_literal(
623 			forge, str, len, 0,
624 			sratom->map->map(sratom->map->handle, lang_uri));
625 		free(lang_uri);
626 	} else {
627 		lv2_atom_forge_string(forge, str, len);
628 	}
629 }
630 
631 static void
read_object(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,ReadMode mode)632 read_object(Sratom*         sratom,
633             LV2_Atom_Forge* forge,
634             SordWorld*      world,
635             SordModel*      model,
636             const SordNode* node,
637             ReadMode        mode)
638 {
639 	LV2_URID_Map* map = sratom->map;
640 	size_t        len = 0;
641 	const char*   str = (const char*)sord_node_get_string_counted(node, &len);
642 
643 	SordNode* type = sord_get(
644 		model, node, sratom->nodes.rdf_type, NULL, NULL);
645 	SordNode* value = sord_get(
646 		model, node, sratom->nodes.rdf_value, NULL, NULL);
647 
648 	const uint8_t* type_uri  = NULL;
649 	uint32_t       type_urid = 0;
650 	if (type) {
651 		type_uri  = sord_node_get_string(type);
652 		type_urid = map->map(map->handle, (const char*)type_uri);
653 	}
654 
655 	LV2_Atom_Forge_Frame frame = { 0, 0 };
656 	if (mode == MODE_SEQUENCE) {
657 		SordNode* time = sord_get(
658 			model, node, sratom->nodes.atom_beatTime, NULL, NULL);
659 		uint32_t seq_unit;
660 		if (time) {
661 			const char* time_str = (const char*)sord_node_get_string(time);
662 			lv2_atom_forge_beat_time(forge, serd_strtod(time_str, NULL));
663 			seq_unit = sratom->atom_beatTime;
664 		} else {
665 			time = sord_get(model, node, sratom->nodes.atom_frameTime, NULL, NULL);
666 			const char* time_str = time
667 				? (const char*)sord_node_get_string(time)
668 				: "";
669 			lv2_atom_forge_frame_time(forge, serd_strtod(time_str, NULL));
670 			seq_unit = sratom->atom_frameTime;
671 		}
672 		read_node(sratom, forge, world, model, value, MODE_BODY);
673 		sord_node_free(world, time);
674 		sratom->seq_unit = seq_unit;
675 	} else if (type_urid == sratom->forge.Tuple) {
676 		lv2_atom_forge_tuple(forge, &frame);
677 		read_list_value(sratom, forge, world, model, value, MODE_BODY);
678 	} else if (type_urid == sratom->forge.Sequence) {
679 		const LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0);
680 		sratom->seq_unit = 0;
681 		read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE);
682 
683 		LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)lv2_atom_forge_deref(forge, ref);
684 		seq->body.unit = (sratom->seq_unit == sratom->atom_frameTime) ? 0 : sratom->seq_unit;
685 	} else if (type_urid == sratom->forge.Vector) {
686 		SordNode* child_type_node = sord_get(
687 			model, node, sratom->nodes.atom_childType, NULL, NULL);
688 		uint32_t child_type = map->map(
689 			map->handle, (const char*)sord_node_get_string(child_type_node));
690 		uint32_t child_size = atom_size(sratom, child_type);
691 		if (child_size > 0) {
692 			LV2_Atom_Forge_Ref ref = lv2_atom_forge_vector_head(
693 				forge, &frame, child_size, child_type);
694 			read_list_value(sratom, forge, world, model, value, MODE_BODY);
695 			lv2_atom_forge_pop(forge, &frame);
696 			frame.ref = 0;
697 			lv2_atom_forge_pad(forge, lv2_atom_forge_deref(forge, ref)->size);
698 		}
699 		sord_node_free(world, child_type_node);
700 	} else if (value && sord_node_equals(sord_node_get_datatype(value),
701 	                                     sratom->nodes.xsd_base64Binary)) {
702 		size_t         vlen = 0;
703 		const uint8_t* vstr = sord_node_get_string_counted(value, &vlen);
704 		size_t         size = 0;
705 		void*          body = serd_base64_decode(vstr, vlen, &size);
706 		lv2_atom_forge_atom(forge, size, type_urid);
707 		lv2_atom_forge_write(forge, body, size);
708 		free(body);
709 	} else if (sord_node_get_type(node) == SORD_URI) {
710 		lv2_atom_forge_object(
711 			forge, &frame, map->map(map->handle, str), type_urid);
712 		read_resource(sratom, forge, world, model, node, type_urid);
713 	} else {
714 		lv2_atom_forge_object(forge, &frame, 0, type_urid);
715 		read_resource(sratom, forge, world, model, node, type_urid);
716 	}
717 
718 	if (frame.ref) {
719 		lv2_atom_forge_pop(forge, &frame);
720 	}
721 	sord_node_free(world, value);
722 	sord_node_free(world, type);
723 }
724 
725 static void
read_node(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node,ReadMode mode)726 read_node(Sratom*         sratom,
727           LV2_Atom_Forge* forge,
728           SordWorld*      world,
729           SordModel*      model,
730           const SordNode* node,
731           ReadMode        mode)
732 {
733 	LV2_URID_Map* map = sratom->map;
734 	size_t        len = 0;
735 	const char*   str = (const char*)sord_node_get_string_counted(node, &len);
736 	if (sord_node_get_type(node) == SORD_LITERAL) {
737 		read_literal(sratom, forge, node);
738 	} else if (sord_node_get_type(node) == SORD_URI &&
739 	           !(sratom->object_mode == SRATOM_OBJECT_MODE_BLANK_SUBJECT
740 	             && mode == MODE_SUBJECT)) {
741 		if (!strcmp(str, (const char*)NS_RDF "nil")) {
742 			lv2_atom_forge_atom(forge, 0, 0);
743 		} else if (!strncmp(str, "file://", 7)) {
744 			SerdURI uri;
745 			serd_uri_parse((const uint8_t*)str, &uri);
746 
747 			SerdNode rel  = serd_node_new_relative_uri(&uri, &sratom->base, NULL, NULL);
748 			uint8_t* path = serd_file_uri_parse(rel.buf, NULL);
749 			lv2_atom_forge_path(forge, (const char*)path, strlen((const char*)path));
750 			serd_free(path);
751 			serd_node_free(&rel);
752 		} else {
753 			lv2_atom_forge_urid(forge, map->map(map->handle, str));
754 		}
755 	} else {
756 		read_object(sratom, forge, world, model, node, mode);
757 	}
758 }
759 
760 void
sratom_read(Sratom * sratom,LV2_Atom_Forge * forge,SordWorld * world,SordModel * model,const SordNode * node)761 sratom_read(Sratom*         sratom,
762             LV2_Atom_Forge* forge,
763             SordWorld*      world,
764             SordModel*      model,
765             const SordNode* node)
766 {
767 	sratom->nodes.atom_childType   = sord_new_uri(world, USTR(LV2_ATOM__childType));
768 	sratom->nodes.atom_frameTime   = sord_new_uri(world, USTR(LV2_ATOM__frameTime));
769 	sratom->nodes.atom_beatTime    = sord_new_uri(world, USTR(LV2_ATOM__beatTime));
770 	sratom->nodes.rdf_first        = sord_new_uri(world, NS_RDF "first");
771 	sratom->nodes.rdf_rest         = sord_new_uri(world, NS_RDF "rest");
772 	sratom->nodes.rdf_type         = sord_new_uri(world, NS_RDF "type");
773 	sratom->nodes.rdf_value        = sord_new_uri(world, NS_RDF "value");
774 	sratom->nodes.xsd_base64Binary = sord_new_uri(world, NS_XSD "base64Binary");
775 
776 	sratom->next_id = 1;
777 	read_node(sratom, forge, world, model, node, MODE_SUBJECT);
778 
779 	sord_node_free(world, sratom->nodes.xsd_base64Binary);
780 	sord_node_free(world, sratom->nodes.rdf_value);
781 	sord_node_free(world, sratom->nodes.rdf_type);
782 	sord_node_free(world, sratom->nodes.rdf_rest);
783 	sord_node_free(world, sratom->nodes.rdf_first);
784 	sord_node_free(world, sratom->nodes.atom_frameTime);
785 	sord_node_free(world, sratom->nodes.atom_beatTime);
786 	sord_node_free(world, sratom->nodes.atom_childType);
787 	memset(&sratom->nodes, 0, sizeof(sratom->nodes));
788 }
789 
790 LV2_Atom_Forge_Ref
sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,const void * buf,uint32_t size)791 sratom_forge_sink(LV2_Atom_Forge_Sink_Handle handle,
792                   const void*                buf,
793                   uint32_t                   size)
794 {
795 	SerdChunk*               chunk = (SerdChunk*)handle;
796 	const LV2_Atom_Forge_Ref ref   = chunk->len + 1;
797 	serd_chunk_sink(buf, size, chunk);
798 	return ref;
799 }
800 
801 LV2_Atom*
sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle,LV2_Atom_Forge_Ref ref)802 sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref)
803 {
804 	SerdChunk* chunk = (SerdChunk*)handle;
805 	return (LV2_Atom*)(chunk->buf + ref - 1);
806 }
807 
808 LV2_Atom*
sratom_from_turtle(Sratom * sratom,const char * base_uri,const SerdNode * subject,const SerdNode * predicate,const char * str)809 sratom_from_turtle(Sratom*         sratom,
810                    const char*     base_uri,
811                    const SerdNode* subject,
812                    const SerdNode* predicate,
813                    const char*     str)
814 {
815 	SerdChunk   out    = { NULL, 0 };
816 	SerdNode    base   = serd_node_new_uri_from_string(USTR(base_uri), &sratom->base, NULL);
817 	SordWorld*  world  = sord_world_new();
818 	SordModel*  model  = sord_new(world, SORD_SPO, false);
819 	SerdEnv*    env    = sratom->env ? sratom->env : serd_env_new(&base);
820 	SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
821 
822 	if (!serd_reader_read_string(reader, (const uint8_t*)str)) {
823 		SordNode* s = sord_node_from_serd_node(world, env, subject, 0, 0);
824 		lv2_atom_forge_set_sink(
825 			&sratom->forge, sratom_forge_sink, sratom_forge_deref, &out);
826 		if (subject && predicate) {
827 			SordNode* p = sord_node_from_serd_node(world, env, predicate, 0, 0);
828 			SordNode* o = sord_get(model, s, p, NULL, NULL);
829 			if (o) {
830 				sratom_read(sratom, &sratom->forge, world, model, o);
831 				sord_node_free(world, o);
832 			} else {
833 				fprintf(stderr, "Failed to find node\n");
834 			}
835 		} else {
836 			sratom_read(sratom, &sratom->forge, world, model, s);
837 		}
838 	} else {
839 		fprintf(stderr, "Failed to read Turtle\n");
840 	}
841 
842 	serd_reader_free(reader);
843 	if (!sratom->env) {
844 		serd_env_free(env);
845 	}
846 	sord_free(model);
847 	sord_world_free(world);
848 	serd_node_free(&base);
849 
850 	return (LV2_Atom*)out.buf;
851 }
852