mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-24 22:30:21 +01:00
Adding new libraries for LV2 support.
lilv-0.16.0 lv2-1.6.0 serd-0.18.2 sord-0.12.0 sratom-0.4.2
This commit is contained in:
271
lib-src/lv2/serd/src/env.c
Normal file
271
lib-src/lv2/serd/src/env.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "serd_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
SerdNode name;
|
||||
SerdNode uri;
|
||||
} SerdPrefix;
|
||||
|
||||
struct SerdEnvImpl {
|
||||
SerdPrefix* prefixes;
|
||||
size_t n_prefixes;
|
||||
SerdNode base_uri_node;
|
||||
SerdURI base_uri;
|
||||
};
|
||||
|
||||
SERD_API
|
||||
SerdEnv*
|
||||
serd_env_new(const SerdNode* base_uri)
|
||||
{
|
||||
SerdEnv* env = (SerdEnv*)malloc(sizeof(struct SerdEnvImpl));
|
||||
env->prefixes = NULL;
|
||||
env->n_prefixes = 0;
|
||||
env->base_uri_node = SERD_NODE_NULL;
|
||||
env->base_uri = SERD_URI_NULL;
|
||||
if (base_uri) {
|
||||
serd_env_set_base_uri(env, base_uri);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void
|
||||
serd_env_free(SerdEnv* env)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
SERD_API
|
||||
const SerdNode*
|
||||
serd_env_get_base_uri(const SerdEnv* env,
|
||||
SerdURI* out)
|
||||
{
|
||||
if (out) {
|
||||
*out = env->base_uri;
|
||||
}
|
||||
return &env->base_uri_node;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_env_set_base_uri(SerdEnv* env,
|
||||
const SerdNode* uri_node)
|
||||
{
|
||||
// Resolve base URI and create a new node and URI for it
|
||||
SerdURI base_uri;
|
||||
SerdNode base_uri_node = serd_node_new_uri_from_node(
|
||||
uri_node, &env->base_uri, &base_uri);
|
||||
|
||||
if (base_uri_node.buf) {
|
||||
// Replace the current base URI
|
||||
serd_node_free(&env->base_uri_node);
|
||||
env->base_uri_node = base_uri_node;
|
||||
env->base_uri = base_uri;
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
return SERD_ERR_BAD_ARG;
|
||||
}
|
||||
|
||||
static inline SerdPrefix*
|
||||
serd_env_find(const SerdEnv* env,
|
||||
const uint8_t* name,
|
||||
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->n_bytes == name_len) {
|
||||
if (!memcmp(prefix_name->buf, name, name_len)) {
|
||||
return &env->prefixes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
serd_env_add(SerdEnv* env,
|
||||
const SerdNode* name,
|
||||
const SerdNode* uri)
|
||||
{
|
||||
SerdPrefix* const prefix = serd_env_find(env, name->buf, name->n_bytes);
|
||||
if (prefix) {
|
||||
SerdNode old_prefix_uri = prefix->uri;
|
||||
prefix->uri = serd_node_copy(uri);
|
||||
serd_node_free(&old_prefix_uri);
|
||||
} else {
|
||||
env->prefixes = (SerdPrefix*)realloc(
|
||||
env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix));
|
||||
env->prefixes[env->n_prefixes - 1].name = serd_node_copy(name);
|
||||
env->prefixes[env->n_prefixes - 1].uri = serd_node_copy(uri);
|
||||
}
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_env_set_prefix(SerdEnv* env,
|
||||
const SerdNode* name,
|
||||
const SerdNode* uri_node)
|
||||
{
|
||||
if (!name->buf || uri_node->type != SERD_URI) {
|
||||
return SERD_ERR_BAD_ARG;
|
||||
} else if (serd_uri_string_has_scheme(uri_node->buf)) {
|
||||
// Set prefix to absolute URI
|
||||
serd_env_add(env, name, uri_node);
|
||||
} else {
|
||||
// Resolve relative URI and create a new node and URI for it
|
||||
SerdURI abs_uri;
|
||||
SerdNode abs_uri_node = serd_node_new_uri_from_node(
|
||||
uri_node, &env->base_uri, &abs_uri);
|
||||
|
||||
// Set prefix to resolved (absolute) URI
|
||||
serd_env_add(env, name, &abs_uri_node);
|
||||
serd_node_free(&abs_uri_node);
|
||||
}
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_env_set_prefix_from_strings(SerdEnv* env,
|
||||
const uint8_t* name,
|
||||
const uint8_t* uri)
|
||||
{
|
||||
const SerdNode name_node = serd_node_from_string(SERD_LITERAL, name);
|
||||
const SerdNode uri_node = serd_node_from_string(SERD_URI, uri);
|
||||
|
||||
return serd_env_set_prefix(env, &name_node, &uri_node);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_nameChar(const uint8_t c)
|
||||
{
|
||||
return is_alpha(c) || is_digit(c) || c == '_';
|
||||
}
|
||||
|
||||
/**
|
||||
Return true iff @c buf is a valid prefixed name suffix.
|
||||
TODO: This is more strict than it should be.
|
||||
*/
|
||||
static inline bool
|
||||
is_name(const uint8_t* buf, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (!is_nameChar(buf[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
bool
|
||||
serd_env_qualify(const SerdEnv* env,
|
||||
const SerdNode* uri,
|
||||
SerdNode* prefix_name,
|
||||
SerdChunk* suffix)
|
||||
{
|
||||
for (size_t i = 0; i < env->n_prefixes; ++i) {
|
||||
const SerdNode* const prefix_uri = &env->prefixes[i].uri;
|
||||
if (uri->n_bytes >= prefix_uri->n_bytes) {
|
||||
if (!strncmp((const char*)uri->buf,
|
||||
(const char*)prefix_uri->buf,
|
||||
prefix_uri->n_bytes)) {
|
||||
*prefix_name = env->prefixes[i].name;
|
||||
suffix->buf = uri->buf + prefix_uri->n_bytes;
|
||||
suffix->len = uri->n_bytes - prefix_uri->n_bytes;
|
||||
if (is_name(suffix->buf, suffix->len)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_env_expand(const SerdEnv* env,
|
||||
const SerdNode* qname,
|
||||
SerdChunk* uri_prefix,
|
||||
SerdChunk* uri_suffix)
|
||||
{
|
||||
const uint8_t* const colon = (const uint8_t*)memchr(
|
||||
qname->buf, ':', qname->n_bytes + 1);
|
||||
if (!colon) {
|
||||
return SERD_ERR_BAD_ARG; // Invalid qname
|
||||
}
|
||||
|
||||
const size_t name_len = colon - qname->buf;
|
||||
const SerdPrefix* const prefix = serd_env_find(env, qname->buf, name_len);
|
||||
if (prefix) {
|
||||
uri_prefix->buf = prefix->uri.buf;
|
||||
uri_prefix->len = prefix->uri.n_bytes;
|
||||
uri_suffix->buf = colon + 1;
|
||||
uri_suffix->len = qname->n_bytes - (colon - qname->buf) - 1;
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
return SERD_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_env_expand_node(const SerdEnv* env,
|
||||
const SerdNode* node)
|
||||
{
|
||||
switch (node->type) {
|
||||
case SERD_CURIE: {
|
||||
SerdChunk prefix;
|
||||
SerdChunk suffix;
|
||||
if (serd_env_expand(env, node, &prefix, &suffix)) {
|
||||
return SERD_NODE_NULL;
|
||||
}
|
||||
const size_t len = prefix.len + suffix.len; // FIXME: UTF-8?
|
||||
uint8_t* buf = (uint8_t*)malloc(len + 1);
|
||||
SerdNode ret = { buf, len, len, 0, SERD_URI };
|
||||
snprintf((char*)buf, ret.n_bytes + 1, "%s%s", prefix.buf, suffix.buf);
|
||||
return ret;
|
||||
}
|
||||
case SERD_URI: {
|
||||
SerdURI ignored;
|
||||
return serd_node_new_uri_from_node(node, &env->base_uri, &ignored);
|
||||
}
|
||||
default:
|
||||
return SERD_NODE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void
|
||||
serd_env_foreach(const SerdEnv* env,
|
||||
SerdPrefixSink func,
|
||||
void* handle)
|
||||
{
|
||||
for (size_t i = 0; i < env->n_prefixes; ++i) {
|
||||
func(handle, &env->prefixes[i].name, &env->prefixes[i].uri);
|
||||
}
|
||||
}
|
||||
347
lib-src/lv2/serd/src/node.c
Normal file
347
lib-src/lv2/serd/src/node.c
Normal file
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "serd_internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# define isnan(x) _isnan(x)
|
||||
# define isinf(x) (!_finite(x))
|
||||
#endif
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_from_string(SerdType type, const uint8_t* buf)
|
||||
{
|
||||
if (!buf) {
|
||||
return SERD_NODE_NULL;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
size_t buf_n_bytes = 0;
|
||||
const size_t buf_n_chars = serd_strlen(buf, &buf_n_bytes, &flags);
|
||||
SerdNode ret = { buf, buf_n_bytes, buf_n_chars, flags, type };
|
||||
return ret;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_copy(const SerdNode* node)
|
||||
{
|
||||
if (!node || !node->buf) {
|
||||
return SERD_NODE_NULL;
|
||||
}
|
||||
|
||||
SerdNode copy = *node;
|
||||
uint8_t* buf = (uint8_t*)malloc(copy.n_bytes + 1);
|
||||
memcpy(buf, node->buf, copy.n_bytes + 1);
|
||||
copy.buf = buf;
|
||||
return copy;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
bool
|
||||
serd_node_equals(const SerdNode* a, const SerdNode* b)
|
||||
{
|
||||
return (a == b)
|
||||
|| (a->type == b->type
|
||||
&& a->n_bytes == b->n_bytes
|
||||
&& a->n_chars == b->n_chars
|
||||
&& ((a->buf == b->buf) || !memcmp((const char*)a->buf,
|
||||
(const char*)b->buf,
|
||||
a->n_bytes + 1)));
|
||||
}
|
||||
|
||||
static size_t
|
||||
serd_uri_string_length(const SerdURI* uri)
|
||||
{
|
||||
size_t len = uri->path_base.len;
|
||||
|
||||
#define ADD_LEN(field, n_delims) \
|
||||
if ((field).len) { len += (field).len + (n_delims); }
|
||||
|
||||
ADD_LEN(uri->path, 1); // + possible leading `/'
|
||||
ADD_LEN(uri->scheme, 1); // + trailing `:'
|
||||
ADD_LEN(uri->authority, 2); // + leading `//'
|
||||
ADD_LEN(uri->query, 1); // + leading `?'
|
||||
ADD_LEN(uri->fragment, 1); // + leading `#'
|
||||
|
||||
return len + 2; // + 2 for authority `//'
|
||||
}
|
||||
|
||||
static size_t
|
||||
string_sink(const void* buf, size_t len, void* stream)
|
||||
{
|
||||
uint8_t** ptr = (uint8_t**)stream;
|
||||
memcpy(*ptr, buf, len);
|
||||
*ptr += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_uri_from_node(const SerdNode* uri_node,
|
||||
const SerdURI* base,
|
||||
SerdURI* out)
|
||||
{
|
||||
return (uri_node->type == SERD_URI)
|
||||
? serd_node_new_uri_from_string(uri_node->buf, base, out)
|
||||
: SERD_NODE_NULL;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_uri_from_string(const uint8_t* str,
|
||||
const SerdURI* base,
|
||||
SerdURI* out)
|
||||
{
|
||||
if (!str || str[0] == '\0') {
|
||||
return serd_node_new_uri(base, NULL, out); // Empty URI => Base URI
|
||||
}
|
||||
SerdURI uri;
|
||||
serd_uri_parse(str, &uri);
|
||||
return serd_node_new_uri(&uri, base, out); // Resolve/Serialise
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_uri_path_char(const uint8_t c)
|
||||
{
|
||||
if (is_alpha(c) || is_digit(c)) {
|
||||
return true;
|
||||
}
|
||||
switch (c) {
|
||||
case '-': case '.': case '_': case '~': // unreserved
|
||||
case ':': case '@': // pchar
|
||||
case '/': // separator
|
||||
// sub-delims
|
||||
case '!': case '$': case '&': case '\'': case '(': case ')':
|
||||
case '*': case '+': case ',': case ';': case '=':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_file_uri(const uint8_t* path,
|
||||
const uint8_t* hostname,
|
||||
SerdURI* out,
|
||||
bool escape)
|
||||
{
|
||||
const size_t path_len = strlen((const char*)path);
|
||||
const size_t hostname_len = hostname ? strlen((const char*)hostname) : 0;
|
||||
const bool evil = is_windows_path(path);
|
||||
size_t uri_len = 0;
|
||||
uint8_t* uri = NULL;
|
||||
|
||||
if (path[0] == '/' || is_windows_path(path)) {
|
||||
uri_len = strlen("file://") + hostname_len + evil;
|
||||
uri = (uint8_t*)malloc(uri_len + 1);
|
||||
snprintf((char*)uri, uri_len + 1, "file://%s%s",
|
||||
hostname ? (const char*)hostname : "",
|
||||
evil ? "/" : "");
|
||||
}
|
||||
|
||||
SerdChunk chunk = { uri, uri_len };
|
||||
for (size_t i = 0; i < path_len; ++i) {
|
||||
if (evil && path[i] == '\\') {
|
||||
serd_chunk_sink("/", 1, &chunk);
|
||||
} else if (path[i] == '%') {
|
||||
serd_chunk_sink("%%", 2, &chunk);
|
||||
} else if (!escape || is_uri_path_char(path[i])) {
|
||||
serd_chunk_sink(path + i, 1, &chunk);
|
||||
} else {
|
||||
char escape_str[4] = { '%', 0, 0, 0 };
|
||||
snprintf(escape_str + 1, sizeof(escape_str) - 1, "%X", path[i]);
|
||||
serd_chunk_sink(escape_str, 3, &chunk);
|
||||
}
|
||||
}
|
||||
serd_chunk_sink_finish(&chunk);
|
||||
|
||||
if (out) {
|
||||
serd_uri_parse(chunk.buf, out);
|
||||
}
|
||||
|
||||
return serd_node_from_string(SERD_URI, chunk.buf);
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out)
|
||||
{
|
||||
SerdURI abs_uri = *uri;
|
||||
if (base) {
|
||||
serd_uri_resolve(uri, base, &abs_uri);
|
||||
}
|
||||
|
||||
const size_t len = serd_uri_string_length(&abs_uri);
|
||||
uint8_t* buf = (uint8_t*)malloc(len + 1);
|
||||
|
||||
SerdNode node = { buf, len, len, 0, SERD_URI }; // FIXME: UTF-8
|
||||
|
||||
uint8_t* ptr = buf;
|
||||
const size_t actual_len = serd_uri_serialise(&abs_uri, string_sink, &ptr);
|
||||
|
||||
buf[actual_len] = '\0';
|
||||
node.n_bytes = actual_len;
|
||||
node.n_chars = actual_len;
|
||||
|
||||
if (out) {
|
||||
serd_uri_parse(buf, out); // TODO: cleverly avoid double parse
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_decimal(double d, unsigned frac_digits)
|
||||
{
|
||||
if (isnan(d) || isinf(d)) {
|
||||
return SERD_NODE_NULL;
|
||||
}
|
||||
|
||||
const double abs_d = fabs(d);
|
||||
const unsigned int_digits = (unsigned)fmax(1.0, ceil(log10(abs_d + 1)));
|
||||
char* buf = (char*)calloc(int_digits + frac_digits + 3, 1);
|
||||
SerdNode node = { (const uint8_t*)buf, 0, 0, 0, SERD_LITERAL };
|
||||
const double int_part = floor(abs_d);
|
||||
|
||||
// Point s to decimal point location
|
||||
char* s = buf + int_digits;
|
||||
if (d < 0.0) {
|
||||
*buf = '-';
|
||||
++s;
|
||||
}
|
||||
|
||||
// Write integer part (right to left)
|
||||
char* t = s - 1;
|
||||
uint64_t dec = (uint64_t)int_part;
|
||||
do {
|
||||
*t-- = '0' + (dec % 10);
|
||||
} while ((dec /= 10) > 0);
|
||||
|
||||
*s++ = '.';
|
||||
|
||||
// Write fractional part (right to left)
|
||||
double frac_part = fabs(d - int_part);
|
||||
if (frac_part < DBL_EPSILON) {
|
||||
*s++ = '0';
|
||||
node.n_bytes = node.n_chars = (s - buf);
|
||||
} else {
|
||||
uint64_t frac = frac_part * pow(10.0, (int)frac_digits) + 0.5;
|
||||
s += frac_digits - 1;
|
||||
unsigned i = 0;
|
||||
|
||||
// Skip trailing zeros
|
||||
for (; i < frac_digits - 1 && !(frac % 10); ++i, --s, frac /= 10) {}
|
||||
|
||||
node.n_bytes = node.n_chars = (s - buf) + 1;
|
||||
|
||||
// Write digits from last trailing zero to decimal point
|
||||
for (; i < frac_digits; ++i) {
|
||||
*s-- = '0' + (frac % 10);
|
||||
frac /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_integer(int64_t i)
|
||||
{
|
||||
int64_t abs_i = (i < 0) ? -i : i;
|
||||
const unsigned digits = fmax(1.0, ceil(log10((double)abs_i + 1)));
|
||||
char* buf = (char*)calloc(digits + 2, 1);
|
||||
SerdNode node = { (const uint8_t*)buf, 0, 0, 0, SERD_LITERAL };
|
||||
|
||||
// Point s to the end
|
||||
char* s = buf + digits - 1;
|
||||
if (i < 0) {
|
||||
*buf = '-';
|
||||
++s;
|
||||
}
|
||||
|
||||
node.n_bytes = node.n_chars = (s - buf) + 1;
|
||||
|
||||
// Write integer part (right to left)
|
||||
do {
|
||||
*s-- = '0' + (abs_i % 10);
|
||||
} while ((abs_i /= 10) > 0);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
Base64 encoding table.
|
||||
@see <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC3986 S3</a>.
|
||||
*/
|
||||
static const uint8_t b64_map[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
Encode 3 raw bytes to 4 base64 characters.
|
||||
*/
|
||||
static inline void
|
||||
encode_chunk(uint8_t out[4], const uint8_t in[3], size_t n_in)
|
||||
{
|
||||
out[0] = b64_map[in[0] >> 2];
|
||||
out[1] = b64_map[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)];
|
||||
out[2] = ((n_in > 1)
|
||||
? (b64_map[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)])
|
||||
: (uint8_t)'=');
|
||||
out[3] = ((n_in > 2) ? b64_map[in[2] & 0x3F] : (uint8_t)'=');
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdNode
|
||||
serd_node_new_blob(const void* buf, size_t size, bool wrap_lines)
|
||||
{
|
||||
const size_t len = ((size + 2) / 3) * 4 + (wrap_lines ? (size / 57) : 0);
|
||||
uint8_t* str = (uint8_t*)calloc(1, len + 2);
|
||||
SerdNode node = { str, len, len, 0, SERD_LITERAL };
|
||||
for (size_t i = 0, j = 0; i < size; i += 3, j += 4) {
|
||||
uint8_t in[4] = { 0, 0, 0, 0 };
|
||||
size_t n_in = MIN(3, size - i);
|
||||
memcpy(in, (const uint8_t*)buf + i, n_in);
|
||||
|
||||
if (wrap_lines && i > 0 && (i % 57) == 0) {
|
||||
str[j++] = '\n';
|
||||
node.flags |= SERD_HAS_NEWLINE;
|
||||
}
|
||||
|
||||
encode_chunk(str + j, in, n_in);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void
|
||||
serd_node_free(SerdNode* node)
|
||||
{
|
||||
if (node->buf) {
|
||||
free((uint8_t*)node->buf);
|
||||
node->buf = NULL;
|
||||
}
|
||||
}
|
||||
1585
lib-src/lv2/serd/src/reader.c
Normal file
1585
lib-src/lv2/serd/src/reader.c
Normal file
File diff suppressed because it is too large
Load Diff
306
lib-src/lv2/serd/src/serd_internal.h
Normal file
306
lib-src/lv2/serd/src/serd_internal.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SERD_INTERNAL_H
|
||||
#define SERD_INTERNAL_H
|
||||
|
||||
#define _POSIX_C_SOURCE 201112L /* for posix_memalign and posix_fadvise */
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "serd/serd.h"
|
||||
#include "serd_config.h"
|
||||
|
||||
#if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO)
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#define SERD_PAGE_SIZE 4096
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_FMAX
|
||||
static inline double
|
||||
fmax(double a, double b)
|
||||
{
|
||||
return (a < b) ? b : a;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* File and Buffer Utilities */
|
||||
|
||||
static inline FILE*
|
||||
serd_fopen(const char* path, const char* mode)
|
||||
{
|
||||
FILE* fd = fopen((const char*)path, mode);
|
||||
if (!fd) {
|
||||
fprintf(stderr, "Error opening file %s (%s)\n", path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
#if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO)
|
||||
posix_fadvise(fileno(fd), 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||
#endif
|
||||
return fd;
|
||||
}
|
||||
|
||||
static inline void*
|
||||
serd_bufalloc(size_t size)
|
||||
{
|
||||
#ifdef HAVE_POSIX_MEMALIGN
|
||||
void* ptr;
|
||||
posix_memalign(&ptr, SERD_PAGE_SIZE, size);
|
||||
return ptr;
|
||||
#else
|
||||
return malloc(size);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Stack */
|
||||
|
||||
/** A dynamic stack in memory. */
|
||||
typedef struct {
|
||||
uint8_t* buf; ///< Stack memory
|
||||
size_t buf_size; ///< Allocated size of buf (>= size)
|
||||
size_t size; ///< Conceptual size of stack in buf
|
||||
} SerdStack;
|
||||
|
||||
/** An offset to start the stack at. Note 0 is reserved for NULL. */
|
||||
#define SERD_STACK_BOTTOM sizeof(void*)
|
||||
|
||||
static inline SerdStack
|
||||
serd_stack_new(size_t size)
|
||||
{
|
||||
SerdStack stack;
|
||||
stack.buf = (uint8_t*)malloc(size);
|
||||
stack.buf_size = size;
|
||||
stack.size = SERD_STACK_BOTTOM;
|
||||
return stack;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
serd_stack_is_empty(SerdStack* stack)
|
||||
{
|
||||
return stack->size <= SERD_STACK_BOTTOM;
|
||||
}
|
||||
|
||||
static inline void
|
||||
serd_stack_free(SerdStack* stack)
|
||||
{
|
||||
free(stack->buf);
|
||||
stack->buf = NULL;
|
||||
stack->buf_size = 0;
|
||||
stack->size = 0;
|
||||
}
|
||||
|
||||
static inline uint8_t*
|
||||
serd_stack_push(SerdStack* stack, size_t n_bytes)
|
||||
{
|
||||
const size_t new_size = stack->size + n_bytes;
|
||||
if (stack->buf_size < new_size) {
|
||||
stack->buf_size *= 2;
|
||||
stack->buf = (uint8_t*)realloc(stack->buf, stack->buf_size);
|
||||
}
|
||||
uint8_t* const ret = (stack->buf + stack->size);
|
||||
stack->size = new_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
serd_stack_pop(SerdStack* stack, size_t n_bytes)
|
||||
{
|
||||
assert(stack->size >= n_bytes);
|
||||
stack->size -= n_bytes;
|
||||
}
|
||||
|
||||
/* Bulk Sink */
|
||||
|
||||
typedef struct SerdBulkSinkImpl {
|
||||
SerdSink sink;
|
||||
void* stream;
|
||||
uint8_t* buf;
|
||||
size_t size;
|
||||
size_t block_size;
|
||||
} SerdBulkSink;
|
||||
|
||||
static inline SerdBulkSink
|
||||
serd_bulk_sink_new(SerdSink sink, void* stream, size_t block_size)
|
||||
{
|
||||
SerdBulkSink bsink;
|
||||
bsink.sink = sink;
|
||||
bsink.stream = stream;
|
||||
bsink.size = 0;
|
||||
bsink.block_size = block_size;
|
||||
bsink.buf = (uint8_t*)serd_bufalloc(block_size);
|
||||
return bsink;
|
||||
}
|
||||
|
||||
static inline void
|
||||
serd_bulk_sink_flush(SerdBulkSink* bsink)
|
||||
{
|
||||
if (bsink->size > 0) {
|
||||
bsink->sink(bsink->buf, bsink->size, bsink->stream);
|
||||
}
|
||||
bsink->size = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
serd_bulk_sink_free(SerdBulkSink* bsink)
|
||||
{
|
||||
serd_bulk_sink_flush(bsink);
|
||||
free(bsink->buf);
|
||||
bsink->buf = NULL;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
serd_bulk_sink_write(const void* buf, size_t len, SerdBulkSink* bsink)
|
||||
{
|
||||
const size_t orig_len = len;
|
||||
while (len) {
|
||||
const size_t space = bsink->block_size - bsink->size;
|
||||
const size_t n = MIN(space, len);
|
||||
|
||||
// Write as much as possible into the remaining buffer space
|
||||
memcpy(bsink->buf + bsink->size, buf, n);
|
||||
bsink->size += n;
|
||||
buf = (const uint8_t*)buf + n;
|
||||
len -= n;
|
||||
|
||||
// Flush page if buffer is full
|
||||
if (bsink->size == bsink->block_size) {
|
||||
bsink->sink(bsink->buf, bsink->block_size, bsink->stream);
|
||||
bsink->size = 0;
|
||||
}
|
||||
}
|
||||
return orig_len;
|
||||
}
|
||||
|
||||
/* Character utilities */
|
||||
|
||||
/** Return true if @a c lies within [min...max] (inclusive) */
|
||||
static inline bool
|
||||
in_range(const uint8_t c, const uint8_t min, const uint8_t max)
|
||||
{
|
||||
return (c >= min && c <= max);
|
||||
}
|
||||
|
||||
/** RFC2234: ALPHA := %x41-5A / %x61-7A ; A-Z / a-z */
|
||||
static inline bool
|
||||
is_alpha(const uint8_t c)
|
||||
{
|
||||
return in_range(c, 'A', 'Z') || in_range(c, 'a', 'z');
|
||||
}
|
||||
|
||||
/** RFC2234: DIGIT ::= %x30-39 ; 0-9 */
|
||||
static inline bool
|
||||
is_digit(const uint8_t c)
|
||||
{
|
||||
return in_range(c, '0', '9');
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_space(const char c)
|
||||
{
|
||||
switch (c) {
|
||||
case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_base64(const uint8_t c)
|
||||
{
|
||||
return is_alpha(c) || is_digit(c) || c == '+' || c == '/' || c == '=';
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_windows_path(const uint8_t* path)
|
||||
{
|
||||
return is_alpha(path[0]) && (path[1] == ':' || path[1] == '|')
|
||||
&& (path[2] == '/' || path[2] == '\\');
|
||||
}
|
||||
|
||||
/* URI utilities */
|
||||
|
||||
static inline bool
|
||||
chunk_equals(const SerdChunk* a, const SerdChunk* b)
|
||||
{
|
||||
return a->len == b->len
|
||||
&& !strncmp((const char*)a->buf, (const char*)b->buf, a->len);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
uri_path_len(const SerdURI* uri)
|
||||
{
|
||||
return uri->path_base.len + uri->path.len;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
uri_path_at(const SerdURI* uri, size_t i)
|
||||
{
|
||||
if (i < uri->path_base.len) {
|
||||
return uri->path_base.buf[i];
|
||||
} else {
|
||||
return uri->path.buf[i - uri->path_base.len];
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true iff @p uri is within the base of @p root */
|
||||
static inline bool
|
||||
uri_is_under(const SerdURI* uri, const SerdURI* root)
|
||||
{
|
||||
if (!root || !uri || !root->scheme.len ||
|
||||
!chunk_equals(&root->scheme, &uri->scheme) ||
|
||||
!chunk_equals(&root->authority, &uri->authority)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool differ = false;
|
||||
const size_t path_len = uri_path_len(uri);
|
||||
const size_t root_len = uri_path_len(root);
|
||||
for (size_t i = 0; i < path_len && i < root_len; ++i) {
|
||||
if (uri_path_at(uri, i) != uri_path_at(root, i)) {
|
||||
differ = true;
|
||||
}
|
||||
if (differ && uri_path_at(root, i) == '/') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Error reporting */
|
||||
|
||||
static inline void
|
||||
serd_error(SerdErrorSink error_sink, void* handle, const SerdError* e)
|
||||
{
|
||||
if (error_sink) {
|
||||
error_sink(handle, e);
|
||||
} else {
|
||||
fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col);
|
||||
vfprintf(stderr, e->fmt, *e->args);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SERD_INTERNAL_H
|
||||
253
lib-src/lv2/serd/src/serdi.c
Normal file
253
lib-src/lv2/serd/src/serdi.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "serd_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
SerdEnv* env;
|
||||
SerdWriter* writer;
|
||||
} State;
|
||||
|
||||
static int
|
||||
print_version(void)
|
||||
{
|
||||
printf("serdi " SERD_VERSION " <http://drobilla.net/software/serd>\n");
|
||||
printf("Copyright 2011-2012 David Robillard <http://drobilla.net>.\n"
|
||||
"License: <http://www.opensource.org/licenses/isc>\n"
|
||||
"This is free software; you are free to change and redistribute it."
|
||||
"\nThere is NO WARRANTY, to the extent permitted by law.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
print_usage(const char* name, bool error)
|
||||
{
|
||||
FILE* const os = error ? stderr : stdout;
|
||||
fprintf(os, "Usage: %s [OPTION]... INPUT [BASE_URI]\n", name);
|
||||
fprintf(os, "Read and write RDF syntax.\n");
|
||||
fprintf(os, "Use - for INPUT to read from standard input.\n\n");
|
||||
fprintf(os, " -b Fast bulk output for large serialisations.\n");
|
||||
fprintf(os, " -c PREFIX Chop PREFIX from matching blank node IDs.\n");
|
||||
fprintf(os, " -e Eat input one character at a time.\n");
|
||||
fprintf(os, " -f Keep full URIs in input (don't qualify).\n");
|
||||
fprintf(os, " -h Display this help and exit.\n");
|
||||
fprintf(os, " -i SYNTAX Input syntax (`turtle' or `ntriples').\n");
|
||||
fprintf(os, " -o SYNTAX Output syntax (`turtle' or `ntriples').\n");
|
||||
fprintf(os, " -p PREFIX Add PREFIX to blank node IDs.\n");
|
||||
fprintf(os, " -q Suppress all output except data.\n");
|
||||
fprintf(os, " -r ROOT_URI Keep relative URIs within ROOT_URI.\n");
|
||||
fprintf(os, " -s INPUT Parse INPUT as string (terminates options).\n");
|
||||
fprintf(os, " -v Display version information and exit.\n");
|
||||
return error ? 1 : 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
set_syntax(SerdSyntax* syntax, const char* name)
|
||||
{
|
||||
if (!strcmp(name, "turtle")) {
|
||||
*syntax = SERD_TURTLE;
|
||||
} else if (!strcmp(name, "ntriples")) {
|
||||
*syntax = SERD_NTRIPLES;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown input format `%s'\n", name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
bad_arg(const char* name, char opt)
|
||||
{
|
||||
fprintf(stderr, "%s: Bad or missing value for -%c\n", name, opt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SerdStatus
|
||||
quiet_error_sink(void* handle, const SerdError* e)
|
||||
{
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return print_usage(argv[0], true);
|
||||
}
|
||||
|
||||
FILE* in_fd = NULL;
|
||||
SerdSyntax input_syntax = SERD_TURTLE;
|
||||
SerdSyntax output_syntax = SERD_NTRIPLES;
|
||||
bool from_file = true;
|
||||
bool bulk_read = true;
|
||||
bool bulk_write = false;
|
||||
bool full_uris = false;
|
||||
bool quiet = false;
|
||||
const uint8_t* in_name = NULL;
|
||||
const uint8_t* add_prefix = NULL;
|
||||
const uint8_t* chop_prefix = NULL;
|
||||
const uint8_t* root_uri = NULL;
|
||||
int a = 1;
|
||||
for (; a < argc && argv[a][0] == '-'; ++a) {
|
||||
if (argv[a][1] == '\0') {
|
||||
in_name = (const uint8_t*)"(stdin)";
|
||||
in_fd = stdin;
|
||||
break;
|
||||
} else if (argv[a][1] == 'b') {
|
||||
bulk_write = true;
|
||||
} else if (argv[a][1] == 'e') {
|
||||
bulk_read = false;
|
||||
} else if (argv[a][1] == 'f') {
|
||||
full_uris = true;
|
||||
} else if (argv[a][1] == 'h') {
|
||||
return print_usage(argv[0], false);
|
||||
} else if (argv[a][1] == 'q') {
|
||||
quiet = true;
|
||||
} else if (argv[a][1] == 'v') {
|
||||
return print_version();
|
||||
} else if (argv[a][1] == 's') {
|
||||
in_name = (const uint8_t*)"(string)";
|
||||
from_file = false;
|
||||
++a;
|
||||
break;
|
||||
} else if (argv[a][1] == 'i') {
|
||||
if (++a == argc || !set_syntax(&input_syntax, argv[a])) {
|
||||
return bad_arg(argv[0], 'i');
|
||||
}
|
||||
} else if (argv[a][1] == 'o') {
|
||||
if (++a == argc || !set_syntax(&output_syntax, argv[a])) {
|
||||
return bad_arg(argv[0], 'o');
|
||||
}
|
||||
} else if (argv[a][1] == 'p') {
|
||||
if (++a == argc) {
|
||||
return bad_arg(argv[0], 'p');
|
||||
}
|
||||
add_prefix = (const uint8_t*)argv[a];
|
||||
} else if (argv[a][1] == 'c') {
|
||||
if (++a == argc) {
|
||||
return bad_arg(argv[0], 'c');
|
||||
}
|
||||
chop_prefix = (const uint8_t*)argv[a];
|
||||
} else if (argv[a][1] == 'r') {
|
||||
if (++a == argc) {
|
||||
return bad_arg(argv[0], 'r');
|
||||
}
|
||||
root_uri = (const uint8_t*)argv[a];
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown option `%s'\n", argv[0], argv[a]);
|
||||
return print_usage(argv[0], true);
|
||||
}
|
||||
}
|
||||
|
||||
if (a == argc) {
|
||||
fprintf(stderr, "%s: Missing input\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const uint8_t* input = (const uint8_t*)argv[a++];
|
||||
if (from_file) {
|
||||
in_name = in_name ? in_name : input;
|
||||
if (!in_fd) {
|
||||
input = serd_uri_to_path(in_name);
|
||||
if (!input || !(in_fd = serd_fopen((const char*)input, "r"))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SerdURI base_uri = SERD_URI_NULL;
|
||||
SerdNode base = SERD_NODE_NULL;
|
||||
if (a < argc) { // Base URI given on command line
|
||||
base = serd_node_new_uri_from_string(
|
||||
(const uint8_t*)argv[a], NULL, &base_uri);
|
||||
} else if (from_file && in_fd != stdin) { // Use input file URI
|
||||
base = serd_node_new_file_uri(input, NULL, &base_uri, false);
|
||||
}
|
||||
|
||||
FILE* out_fd = stdout;
|
||||
SerdEnv* env = serd_env_new(&base);
|
||||
|
||||
int output_style = 0;
|
||||
if (output_syntax == SERD_NTRIPLES) {
|
||||
output_style |= SERD_STYLE_ASCII;
|
||||
} else {
|
||||
output_style |= SERD_STYLE_ABBREVIATED;
|
||||
if (!full_uris) {
|
||||
output_style |= SERD_STYLE_CURIED;
|
||||
}
|
||||
}
|
||||
|
||||
if (input_syntax != SERD_NTRIPLES // Base URI may change (@base)
|
||||
|| (output_syntax == SERD_TURTLE)) {
|
||||
output_style |= SERD_STYLE_RESOLVED;
|
||||
}
|
||||
|
||||
if (bulk_write) {
|
||||
output_style |= SERD_STYLE_BULK;
|
||||
}
|
||||
|
||||
SerdWriter* writer = serd_writer_new(
|
||||
output_syntax, (SerdStyle)output_style,
|
||||
env, &base_uri, serd_file_sink, out_fd);
|
||||
|
||||
SerdReader* reader = serd_reader_new(
|
||||
input_syntax, writer, NULL,
|
||||
(SerdBaseSink)serd_writer_set_base_uri,
|
||||
(SerdPrefixSink)serd_writer_set_prefix,
|
||||
(SerdStatementSink)serd_writer_write_statement,
|
||||
(SerdEndSink)serd_writer_end_anon);
|
||||
|
||||
if (quiet) {
|
||||
serd_reader_set_error_sink(reader, quiet_error_sink, NULL);
|
||||
serd_writer_set_error_sink(writer, quiet_error_sink, NULL);
|
||||
}
|
||||
|
||||
SerdNode root = serd_node_from_string(SERD_URI, root_uri);
|
||||
serd_writer_set_root_uri(writer, &root);
|
||||
serd_writer_chop_blank_prefix(writer, chop_prefix);
|
||||
serd_reader_add_blank_prefix(reader, add_prefix);
|
||||
|
||||
SerdStatus status = SERD_SUCCESS;
|
||||
if (!from_file) {
|
||||
status = serd_reader_read_string(reader, input);
|
||||
} else if (bulk_read) {
|
||||
status = serd_reader_read_file_handle(reader, in_fd, in_name);
|
||||
} else {
|
||||
status = serd_reader_start_stream(reader, in_fd, in_name, false);
|
||||
while (!status) {
|
||||
status = serd_reader_read_chunk(reader);
|
||||
}
|
||||
serd_reader_end_stream(reader);
|
||||
}
|
||||
|
||||
serd_reader_free(reader);
|
||||
|
||||
if (from_file) {
|
||||
fclose(in_fd);
|
||||
}
|
||||
|
||||
serd_writer_finish(writer);
|
||||
serd_writer_free(writer);
|
||||
serd_env_free(env);
|
||||
serd_node_free(&base);
|
||||
|
||||
return (status > SERD_FAILURE) ? 1 : 0;
|
||||
}
|
||||
165
lib-src/lv2/serd/src/string.c
Normal file
165
lib-src/lv2/serd/src/string.c
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "serd_internal.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
SERD_API
|
||||
const uint8_t*
|
||||
serd_strerror(SerdStatus st)
|
||||
{
|
||||
switch (st) {
|
||||
case SERD_SUCCESS: return (const uint8_t*)"Success";
|
||||
case SERD_FAILURE: return (const uint8_t*)"Non-fatal failure";
|
||||
case SERD_ERR_UNKNOWN: return (const uint8_t*)"Unknown error";
|
||||
case SERD_ERR_BAD_SYNTAX: return (const uint8_t*)"Invalid syntax";
|
||||
case SERD_ERR_BAD_ARG: return (const uint8_t*)"Invalid argument";
|
||||
case SERD_ERR_NOT_FOUND: return (const uint8_t*)"Not found";
|
||||
case SERD_ERR_ID_CLASH: return (const uint8_t*)"Blank node ID clash";
|
||||
case SERD_ERR_BAD_CURIE: return (const uint8_t*)"Invalid CURIE";
|
||||
case SERD_ERR_INTERNAL: return (const uint8_t*)"Internal error";
|
||||
}
|
||||
return (const uint8_t*)"Unknown error"; // never reached
|
||||
}
|
||||
|
||||
SERD_API
|
||||
size_t
|
||||
serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags)
|
||||
{
|
||||
size_t n_chars = 0;
|
||||
size_t i = 0;
|
||||
*flags = 0;
|
||||
for (; str[i]; ++i) {
|
||||
if ((str[i] & 0xC0) != 0x80) {
|
||||
// Does not start with `10', start of a new character
|
||||
++n_chars;
|
||||
switch (str[i]) {
|
||||
case '\r': case '\n':
|
||||
*flags |= SERD_HAS_NEWLINE;
|
||||
break;
|
||||
case '"':
|
||||
*flags |= SERD_HAS_QUOTE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n_bytes) {
|
||||
*n_bytes = i;
|
||||
}
|
||||
return n_chars;
|
||||
}
|
||||
|
||||
static inline double
|
||||
read_sign(const char** sptr)
|
||||
{
|
||||
double sign = 1.0;
|
||||
switch (**sptr) {
|
||||
case '-': sign = -1.0;
|
||||
case '+': ++(*sptr);
|
||||
default: return sign;
|
||||
}
|
||||
}
|
||||
|
||||
SERD_API
|
||||
double
|
||||
serd_strtod(const char* str, char** endptr)
|
||||
{
|
||||
double result = 0.0;
|
||||
|
||||
// Point s at the first non-whitespace character
|
||||
const char* s = str;
|
||||
while (is_space(*s)) { ++s; }
|
||||
|
||||
// Read leading sign if necessary
|
||||
const double sign = read_sign(&s);
|
||||
|
||||
// Parse integer part
|
||||
for (; is_digit(*s); ++s) {
|
||||
result = (result * 10.0) + (*s - '0');
|
||||
}
|
||||
|
||||
// Parse fractional part
|
||||
if (*s == '.') {
|
||||
double denom = 10.0;
|
||||
for (++s; is_digit(*s); ++s) {
|
||||
result += (*s - '0') / denom;
|
||||
denom *= 10.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse exponent
|
||||
if (*s == 'e' || *s == 'E') {
|
||||
++s;
|
||||
double expt = 0.0;
|
||||
double expt_sign = read_sign(&s);
|
||||
for (; is_digit(*s); ++s) {
|
||||
expt = (expt * 10.0) + (*s - '0');
|
||||
}
|
||||
result *= pow(10, expt * expt_sign);
|
||||
}
|
||||
|
||||
if (endptr) {
|
||||
*endptr = (char*)s;
|
||||
}
|
||||
|
||||
return result * sign;
|
||||
}
|
||||
|
||||
/**
|
||||
Base64 decoding table.
|
||||
This is indexed by encoded characters and returns the numeric value used
|
||||
for decoding, shifted up by 47 to be in the range of printable ASCII.
|
||||
A '$' is a placeholder for characters not in the base64 alphabet.
|
||||
*/
|
||||
static const char b64_unmap[] =
|
||||
"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$"
|
||||
"$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$"
|
||||
"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"
|
||||
"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$";
|
||||
|
||||
static inline uint8_t unmap(const uint8_t in) { return b64_unmap[in] - 47; }
|
||||
|
||||
/**
|
||||
Decode 4 base64 characters to 3 raw bytes.
|
||||
*/
|
||||
static inline size_t
|
||||
decode_chunk(const uint8_t in[4], uint8_t out[3])
|
||||
{
|
||||
out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4);
|
||||
out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2);
|
||||
out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3]));
|
||||
return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '='));
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void*
|
||||
serd_base64_decode(const uint8_t* str, size_t len, size_t* size)
|
||||
{
|
||||
void* buf = malloc((len * 3) / 4 + 2);
|
||||
*size = 0;
|
||||
for (size_t i = 0, j = 0; i < len; j += 3) {
|
||||
uint8_t in[] = "====";
|
||||
size_t n_in = 0;
|
||||
for (; i < len && n_in < 4; ++n_in) {
|
||||
for (; i < len && !is_base64(str[i]); ++i) {} // Skip junk
|
||||
in[n_in] = str[i++];
|
||||
}
|
||||
if (n_in > 1) {
|
||||
*size += decode_chunk(in, (uint8_t*)buf + j);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
504
lib-src/lv2/serd/src/uri.c
Normal file
504
lib-src/lv2/serd/src/uri.c
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "serd_internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// #define URI_DEBUG 1
|
||||
|
||||
SERD_API
|
||||
const uint8_t*
|
||||
serd_uri_to_path(const uint8_t* uri)
|
||||
{
|
||||
const uint8_t* path = uri;
|
||||
if (!is_windows_path(uri) && serd_uri_string_has_scheme(uri)) {
|
||||
if (strncmp((const char*)uri, "file:", 5)) {
|
||||
fprintf(stderr, "Non-file URI `%s'\n", uri);
|
||||
return NULL;
|
||||
} else if (!strncmp((const char*)uri, "file://localhost/", 17)) {
|
||||
path = uri + 16;
|
||||
} else if (!strncmp((const char*)uri, "file://", 7)) {
|
||||
path = uri + 7;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid file URI `%s'\n", uri);
|
||||
return NULL;
|
||||
}
|
||||
if (is_windows_path(path + 1)) {
|
||||
++path; // Special case for terrible Windows file URIs
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
uint8_t*
|
||||
serd_file_uri_parse(const uint8_t* uri, uint8_t** hostname)
|
||||
{
|
||||
const uint8_t* path = uri;
|
||||
if (hostname) {
|
||||
*hostname = NULL;
|
||||
}
|
||||
if (!strncmp((const char*)uri, "file://", 7)) {
|
||||
const uint8_t* auth = uri + 7;
|
||||
if (*auth == '/') { // No hostname
|
||||
path = auth;
|
||||
} else { // Has hostname
|
||||
if (!(path = (const uint8_t*)strchr((const char*)auth, '/'))) {
|
||||
return NULL;
|
||||
}
|
||||
if (hostname) {
|
||||
*hostname = (uint8_t*)calloc(1, path - auth + 1);
|
||||
memcpy(*hostname, auth, path - auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_windows_path(path + 1)) {
|
||||
++path;
|
||||
}
|
||||
|
||||
SerdChunk chunk = { NULL, 0 };
|
||||
for (const uint8_t* s = path; *s; ++s) {
|
||||
if (*s == '%') {
|
||||
if (*(s + 1) == '%') {
|
||||
serd_chunk_sink("%", 1, &chunk);
|
||||
++s;
|
||||
} else if (is_digit(*(s + 1)) && is_digit(*(s + 2))) {
|
||||
const uint8_t code[3] = { *(s + 1), *(s + 2), 0 };
|
||||
uint32_t num;
|
||||
sscanf((const char*)code, "%X", &num);
|
||||
const uint8_t c = num;
|
||||
serd_chunk_sink(&c, 1, &chunk);
|
||||
s += 2;
|
||||
} else {
|
||||
s += 2; // Junk escape, ignore
|
||||
}
|
||||
} else {
|
||||
serd_chunk_sink(s, 1, &chunk);
|
||||
}
|
||||
}
|
||||
return serd_chunk_sink_finish(&chunk);
|
||||
}
|
||||
|
||||
SERD_API
|
||||
bool
|
||||
serd_uri_string_has_scheme(const uint8_t* utf8)
|
||||
{
|
||||
// RFC3986: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
if (!is_alpha(utf8[0])) {
|
||||
return false; // Invalid scheme initial character, URI is relative
|
||||
}
|
||||
for (uint8_t c; (c = *++utf8) != '\0';) {
|
||||
switch (c) {
|
||||
case ':':
|
||||
return true; // End of scheme
|
||||
case '+': case '-': case '.':
|
||||
break; // Valid scheme character, continue
|
||||
default:
|
||||
if (!is_alpha(c) && !is_digit(c)) {
|
||||
return false; // Invalid scheme character
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef URI_DEBUG
|
||||
static void
|
||||
serd_uri_dump(const SerdURI* uri, FILE* file)
|
||||
{
|
||||
#define PRINT_PART(range, name) \
|
||||
if (range.buf) { \
|
||||
fprintf(stderr, " " name " = "); \
|
||||
fwrite((range).buf, 1, (range).len, stderr); \
|
||||
fprintf(stderr, "\n"); \
|
||||
}
|
||||
|
||||
PRINT_PART(uri->scheme, "scheme ");
|
||||
PRINT_PART(uri->authority, "authority");
|
||||
PRINT_PART(uri->path_base, "path_base");
|
||||
PRINT_PART(uri->path, "path ");
|
||||
PRINT_PART(uri->query, "query ");
|
||||
PRINT_PART(uri->fragment, "fragment ");
|
||||
}
|
||||
#endif
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_uri_parse(const uint8_t* utf8, SerdURI* uri)
|
||||
{
|
||||
*uri = SERD_URI_NULL;
|
||||
|
||||
const uint8_t* ptr = utf8;
|
||||
|
||||
/* See http://tools.ietf.org/html/rfc3986#section-3
|
||||
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
|
||||
*/
|
||||
|
||||
/* S3.1: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
|
||||
if (is_alpha(*ptr)) {
|
||||
for (uint8_t c = *++ptr; true; c = *++ptr) {
|
||||
switch (c) {
|
||||
case '\0': case '/': case '?': case '#':
|
||||
ptr = utf8;
|
||||
goto path; // Relative URI (starts with path by definition)
|
||||
case ':':
|
||||
uri->scheme.buf = utf8;
|
||||
uri->scheme.len = (ptr++) - utf8;
|
||||
goto maybe_authority; // URI with scheme
|
||||
case '+': case '-': case '.':
|
||||
continue;
|
||||
default:
|
||||
if (is_alpha(c) || is_digit(c)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* S3.2: The authority component is preceded by a double slash ("//")
|
||||
and is terminated by the next slash ("/"), question mark ("?"),
|
||||
or number sign ("#") character, or by the end of the URI.
|
||||
*/
|
||||
maybe_authority:
|
||||
if (*ptr == '/' && *(ptr + 1) == '/') {
|
||||
ptr += 2;
|
||||
uri->authority.buf = ptr;
|
||||
for (uint8_t c; (c = *ptr) != '\0'; ++ptr) {
|
||||
switch (c) {
|
||||
case '/': goto path;
|
||||
case '?': goto query;
|
||||
case '#': goto fragment;
|
||||
default:
|
||||
++uri->authority.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC3986 S3.3: The path is terminated by the first question mark ("?")
|
||||
or number sign ("#") character, or by the end of the URI.
|
||||
*/
|
||||
path:
|
||||
switch (*ptr) {
|
||||
case '?': goto query;
|
||||
case '#': goto fragment;
|
||||
case '\0': goto end;
|
||||
default: break;
|
||||
}
|
||||
uri->path.buf = ptr;
|
||||
uri->path.len = 0;
|
||||
for (uint8_t c; (c = *ptr) != '\0'; ++ptr) {
|
||||
switch (c) {
|
||||
case '?': goto query;
|
||||
case '#': goto fragment;
|
||||
default:
|
||||
++uri->path.len;
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC3986 S3.4: The query component is indicated by the first question
|
||||
mark ("?") character and terminated by a number sign ("#") character
|
||||
or by the end of the URI.
|
||||
*/
|
||||
query:
|
||||
if (*ptr == '?') {
|
||||
uri->query.buf = ++ptr;
|
||||
for (uint8_t c; (c = *ptr) != '\0'; ++ptr) {
|
||||
switch (c) {
|
||||
case '#':
|
||||
goto fragment;
|
||||
default:
|
||||
++uri->query.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* RFC3986 S3.5: A fragment identifier component is indicated by the
|
||||
presence of a number sign ("#") character and terminated by the end
|
||||
of the URI.
|
||||
*/
|
||||
fragment:
|
||||
if (*ptr == '#') {
|
||||
uri->fragment.buf = ptr;
|
||||
while (*ptr++ != '\0') {
|
||||
++uri->fragment.len;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
#ifdef URI_DEBUG
|
||||
fprintf(stderr, "PARSE URI <%s>\n", utf8);
|
||||
serd_uri_dump(uri, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Remove leading dot components from @c path.
|
||||
See http://tools.ietf.org/html/rfc3986#section-5.2.3
|
||||
@param up Set to the number of up-references (e.g. "../") trimmed
|
||||
@return A pointer to the new start of @path
|
||||
*/
|
||||
static const uint8_t*
|
||||
remove_dot_segments(const uint8_t* path, size_t len, size_t* up)
|
||||
{
|
||||
const uint8_t* begin = path;
|
||||
const uint8_t* const end = path + len;
|
||||
|
||||
*up = 0;
|
||||
while (begin < end) {
|
||||
switch (begin[0]) {
|
||||
case '.':
|
||||
switch (begin[1]) {
|
||||
case '/':
|
||||
begin += 2; // Chop leading "./"
|
||||
break;
|
||||
case '.':
|
||||
switch (begin[2]) {
|
||||
case '\0':
|
||||
++*up;
|
||||
begin += 2; // Chop input ".."
|
||||
break;
|
||||
case '/':
|
||||
++*up;
|
||||
begin += 3; // Chop leading "../"
|
||||
break;
|
||||
default:
|
||||
return begin;
|
||||
}
|
||||
break;
|
||||
case '\0':
|
||||
++begin; // Chop input "." (and fall-through)
|
||||
default:
|
||||
return begin;
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
switch (begin[1]) {
|
||||
case '.':
|
||||
switch (begin[2]) {
|
||||
case '/':
|
||||
begin += 2; // Leading "/./" => "/"
|
||||
break;
|
||||
case '.':
|
||||
switch (begin[3]) {
|
||||
case '/':
|
||||
++*up;
|
||||
begin += 3; // Leading "/../" => "/"
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return begin;
|
||||
}
|
||||
} // else fall through
|
||||
default:
|
||||
return begin; // Finished chopping dot components
|
||||
}
|
||||
}
|
||||
|
||||
return begin;
|
||||
}
|
||||
|
||||
/// Merge @p base and @p path in-place
|
||||
static void
|
||||
merge(SerdChunk* base, SerdChunk* path)
|
||||
{
|
||||
size_t up;
|
||||
const uint8_t* begin = remove_dot_segments(path->buf, path->len, &up);
|
||||
const uint8_t* end = path->buf + path->len;
|
||||
|
||||
if (base->buf && base->len > 0) {
|
||||
// Find the up'th last slash
|
||||
const uint8_t* base_last = (base->buf + base->len - 1);
|
||||
++up;
|
||||
do {
|
||||
if (*base_last == '/') {
|
||||
--up;
|
||||
}
|
||||
} while (up > 0 && (--base_last > base->buf));
|
||||
|
||||
// Set path prefix
|
||||
base->len = base_last - base->buf + 1;
|
||||
}
|
||||
|
||||
// Set path suffix
|
||||
path->buf = begin;
|
||||
path->len = end - begin;
|
||||
}
|
||||
|
||||
/// See http://tools.ietf.org/html/rfc3986#section-5.2.2
|
||||
SERD_API
|
||||
void
|
||||
serd_uri_resolve(const SerdURI* r, const SerdURI* base, SerdURI* t)
|
||||
{
|
||||
if (!base->scheme.len) {
|
||||
*t = *r; // Don't resolve against non-absolute URIs
|
||||
return;
|
||||
}
|
||||
|
||||
t->path_base.buf = NULL;
|
||||
t->path_base.len = 0;
|
||||
if (r->scheme.len) {
|
||||
*t = *r;
|
||||
} else {
|
||||
if (r->authority.len) {
|
||||
t->authority = r->authority;
|
||||
t->path = r->path;
|
||||
t->query = r->query;
|
||||
} else {
|
||||
t->path = r->path;
|
||||
if (!r->path.len) {
|
||||
t->path_base = base->path;
|
||||
if (r->query.len) {
|
||||
t->query = r->query;
|
||||
} else {
|
||||
t->query = base->query;
|
||||
}
|
||||
} else {
|
||||
if (r->path.buf[0] != '/') {
|
||||
t->path_base = base->path;
|
||||
}
|
||||
merge(&t->path_base, &t->path);
|
||||
t->query = r->query;
|
||||
}
|
||||
t->authority = base->authority;
|
||||
}
|
||||
t->scheme = base->scheme;
|
||||
t->fragment = r->fragment;
|
||||
}
|
||||
|
||||
#ifdef URI_DEBUG
|
||||
fprintf(stderr, "## RESOLVE URI\n# BASE\n");
|
||||
serd_uri_dump(base, stderr);
|
||||
fprintf(stderr, "# URI\n");
|
||||
serd_uri_dump(r, stderr);
|
||||
fprintf(stderr, "# RESULT\n");
|
||||
serd_uri_dump(t, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Write the path of @p uri starting at index @p i */
|
||||
static size_t
|
||||
write_path_tail(SerdSink sink, void* stream, const SerdURI* uri, size_t i)
|
||||
{
|
||||
size_t len = 0;
|
||||
if (i < uri->path_base.len) {
|
||||
len += sink(uri->path_base.buf + i, uri->path_base.len - i, stream);
|
||||
}
|
||||
if (uri->path.buf) {
|
||||
if (i < uri->path_base.len) {
|
||||
len += sink(uri->path.buf, uri->path.len, stream);
|
||||
} else {
|
||||
const size_t j = (i - uri->path_base.len);
|
||||
len += sink(uri->path.buf + j, uri->path.len - j, stream);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/** Write the path of @p uri relative to the path of @p base. */
|
||||
static size_t
|
||||
write_rel_path(SerdSink sink,
|
||||
void* stream,
|
||||
const SerdURI* uri,
|
||||
const SerdURI* base)
|
||||
{
|
||||
const size_t path_len = uri_path_len(uri);
|
||||
const size_t base_len = uri_path_len(base);
|
||||
const size_t min_len = (path_len < base_len) ? path_len : base_len;
|
||||
|
||||
// Find the last separator common to both paths
|
||||
size_t last_shared_sep = 0;
|
||||
size_t i = 0;
|
||||
for (; i < min_len && uri_path_at(uri, i) == uri_path_at(base, i); ++i) {
|
||||
if (uri_path_at(uri, i) == '/') {
|
||||
last_shared_sep = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == path_len && i == base_len) { // Paths are identical
|
||||
return 0;
|
||||
} else if (last_shared_sep == 0) { // No common components
|
||||
return write_path_tail(sink, stream, uri, 0);
|
||||
}
|
||||
|
||||
// Find the number of up references ("..") required
|
||||
size_t up = 0;
|
||||
for (size_t s = last_shared_sep + 1; s < base_len; ++s) {
|
||||
if (uri_path_at(base, s) == '/') {
|
||||
++up;
|
||||
}
|
||||
}
|
||||
|
||||
// Write up references
|
||||
size_t len = 0;
|
||||
for (size_t u = 0; u < up; ++u) {
|
||||
len += sink("../", 3, stream);
|
||||
}
|
||||
|
||||
// Write suffix
|
||||
return len += write_path_tail(sink, stream, uri, last_shared_sep + 1);
|
||||
}
|
||||
|
||||
/// See http://tools.ietf.org/html/rfc3986#section-5.3
|
||||
SERD_API
|
||||
size_t
|
||||
serd_uri_serialise_relative(const SerdURI* uri,
|
||||
const SerdURI* base,
|
||||
const SerdURI* root,
|
||||
SerdSink sink,
|
||||
void* stream)
|
||||
{
|
||||
size_t len = 0;
|
||||
const bool relative = uri_is_under(uri, root ? root : base);
|
||||
if (relative) {
|
||||
len = write_rel_path(sink, stream, uri, base);
|
||||
}
|
||||
if (!relative || (!len && base->query.buf)) {
|
||||
if (uri->scheme.buf) {
|
||||
len += sink(uri->scheme.buf, uri->scheme.len, stream);
|
||||
len += sink(":", 1, stream);
|
||||
}
|
||||
if (uri->authority.buf) {
|
||||
len += sink("//", 2, stream);
|
||||
len += sink(uri->authority.buf, uri->authority.len, stream);
|
||||
}
|
||||
len += write_path_tail(sink, stream, uri, 0);
|
||||
}
|
||||
if (uri->query.buf) {
|
||||
len += sink("?", 1, stream);
|
||||
len += sink(uri->query.buf, uri->query.len, stream);
|
||||
}
|
||||
if (uri->fragment.buf) {
|
||||
// Note uri->fragment.buf includes the leading `#'
|
||||
len += sink(uri->fragment.buf, uri->fragment.len, stream);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/// See http://tools.ietf.org/html/rfc3986#section-5.3
|
||||
SERD_API
|
||||
size_t
|
||||
serd_uri_serialise(const SerdURI* uri, SerdSink sink, void* stream)
|
||||
{
|
||||
return serd_uri_serialise_relative(uri, NULL, NULL, sink, stream);
|
||||
}
|
||||
807
lib-src/lv2/serd/src/writer.c
Normal file
807
lib-src/lv2/serd/src/writer.c
Normal file
@@ -0,0 +1,807 @@
|
||||
/*
|
||||
Copyright 2011-2012 David Robillard <http://drobilla.net>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "serd_internal.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
|
||||
|
||||
typedef struct {
|
||||
SerdNode graph;
|
||||
SerdNode subject;
|
||||
SerdNode predicate;
|
||||
} WriteContext;
|
||||
|
||||
static const WriteContext WRITE_CONTEXT_NULL = {
|
||||
{ 0, 0, 0, 0, SERD_NOTHING },
|
||||
{ 0, 0, 0, 0, SERD_NOTHING },
|
||||
{ 0, 0, 0, 0, SERD_NOTHING }
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SEP_NONE,
|
||||
SEP_END_S, ///< End of a subject ('.')
|
||||
SEP_END_P, ///< End of a predicate (';')
|
||||
SEP_END_O, ///< End of an object (',')
|
||||
SEP_S_P, ///< Between a subject and predicate (whitespace)
|
||||
SEP_P_O, ///< Between a predicate and object (whitespace)
|
||||
SEP_ANON_BEGIN, ///< Start of anonymous node ('[')
|
||||
SEP_ANON_END, ///< End of anonymous node (']')
|
||||
SEP_LIST_BEGIN, ///< Start of list ('(')
|
||||
SEP_LIST_SEP, ///< List separator (whitespace)
|
||||
SEP_LIST_END ///< End of list (')')
|
||||
} Sep;
|
||||
|
||||
typedef struct {
|
||||
const char* str; ///< Sep string
|
||||
uint8_t len; ///< Length of sep string
|
||||
uint8_t space_before; ///< Newline before sep
|
||||
uint8_t space_after_node; ///< Newline after sep if after node
|
||||
uint8_t space_after_sep; ///< Newline after sep if after sep
|
||||
} SepRule;
|
||||
|
||||
static const SepRule rules[] = {
|
||||
{ NULL, 0, 0, 0, 0 },
|
||||
{ " .\n\n", 4, 0, 0, 0 },
|
||||
{ " ;", 2, 0, 1, 1 },
|
||||
{ " ,", 2, 0, 1, 0 },
|
||||
{ NULL, 0, 0, 1, 0 },
|
||||
{ " ", 1, 0, 0, 0 },
|
||||
{ "[", 1, 0, 1, 1 },
|
||||
{ "]", 1, 1, 0, 0 },
|
||||
{ "(", 1, 0, 0, 0 },
|
||||
{ NULL, 1, 0, 1, 0 },
|
||||
{ ")", 1, 1, 0, 0 },
|
||||
{ "\n", 1, 0, 1, 0 }
|
||||
};
|
||||
|
||||
struct SerdWriterImpl {
|
||||
SerdSyntax syntax;
|
||||
SerdStyle style;
|
||||
SerdEnv* env;
|
||||
SerdNode root_node;
|
||||
SerdURI root_uri;
|
||||
SerdURI base_uri;
|
||||
SerdStack anon_stack;
|
||||
SerdBulkSink bulk_sink;
|
||||
SerdSink sink;
|
||||
void* stream;
|
||||
SerdErrorSink error_sink;
|
||||
void* error_handle;
|
||||
WriteContext context;
|
||||
SerdNode list_subj;
|
||||
unsigned list_depth;
|
||||
uint8_t* bprefix;
|
||||
size_t bprefix_len;
|
||||
unsigned indent;
|
||||
Sep last_sep;
|
||||
bool empty;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WRITE_URI,
|
||||
WRITE_STRING,
|
||||
WRITE_LONG_STRING
|
||||
} TextContext;
|
||||
|
||||
static void
|
||||
w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const SerdError e = { st, NULL, 0, 0, fmt, &args };
|
||||
serd_error(writer->error_sink, writer->error_handle, &e);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static inline WriteContext*
|
||||
anon_stack_top(SerdWriter* writer)
|
||||
{
|
||||
assert(!serd_stack_is_empty(&writer->anon_stack));
|
||||
return (WriteContext*)(writer->anon_stack.buf
|
||||
+ writer->anon_stack.size - sizeof(WriteContext));
|
||||
}
|
||||
|
||||
static void
|
||||
copy_node(SerdNode* dst, const SerdNode* src)
|
||||
{
|
||||
if (src) {
|
||||
dst->buf = (uint8_t*)realloc((char*)dst->buf, src->n_bytes + 1);
|
||||
dst->n_bytes = src->n_bytes;
|
||||
dst->n_chars = src->n_chars;
|
||||
dst->flags = src->flags;
|
||||
dst->type = src->type;
|
||||
memcpy((char*)dst->buf, src->buf, src->n_bytes + 1);
|
||||
} else {
|
||||
dst->type = SERD_NOTHING;
|
||||
}
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
sink(const void* buf, size_t len, SerdWriter* writer)
|
||||
{
|
||||
if (writer->style & SERD_STYLE_BULK) {
|
||||
return serd_bulk_sink_write(buf, len, &writer->bulk_sink);
|
||||
} else {
|
||||
return writer->sink(buf, len, writer->stream);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
write_text(SerdWriter* writer, TextContext ctx,
|
||||
const uint8_t* utf8, size_t n_bytes)
|
||||
{
|
||||
size_t len = 0;
|
||||
char escape[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
for (size_t i = 0; i < n_bytes;) {
|
||||
// Fast bulk write for long strings of printable ASCII
|
||||
size_t j = i;
|
||||
for (; j < n_bytes; ++j) {
|
||||
if (utf8[j] == '>' || utf8[j] == '\\' || utf8[j] == '"'
|
||||
|| (!in_range(utf8[j], 0x20, 0x7E))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j > i) {
|
||||
len += sink(&utf8[i], j - i, writer);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t in = utf8[i++];
|
||||
if (ctx == WRITE_LONG_STRING) {
|
||||
if (in == '\\') {
|
||||
len += sink("\\\\", 2, writer); continue;
|
||||
} else if (in == '\"' && i == n_bytes) {
|
||||
len += sink("\\\"", 2, writer); continue; // '"' at string end
|
||||
}
|
||||
} else {
|
||||
switch (in) {
|
||||
case '\\': len += sink("\\\\", 2, writer); continue;
|
||||
case '\n': len += sink("\\n", 2, writer); continue;
|
||||
case '\r': len += sink("\\r", 2, writer); continue;
|
||||
case '\t': len += sink("\\t", 2, writer); continue;
|
||||
case '"':
|
||||
if (ctx == WRITE_STRING) {
|
||||
len += sink("\\\"", 2, writer);
|
||||
continue;
|
||||
} // else fall-through
|
||||
default: break;
|
||||
}
|
||||
|
||||
if ((ctx == WRITE_STRING && in == '"') ||
|
||||
(ctx == WRITE_URI && in == '>')) {
|
||||
snprintf(escape, sizeof(escape), "\\u%04X",
|
||||
ctx == WRITE_STRING ? '"' : '>');
|
||||
len += sink(escape, 6, writer);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t c = 0;
|
||||
size_t size = 0;
|
||||
if ((in & 0x80) == 0) { // Starts with `0'
|
||||
c = in & 0x7F;
|
||||
if (in_range(c, 0x20, 0x7E)
|
||||
|| (is_space(c) && ctx == WRITE_LONG_STRING)) {
|
||||
len += sink(&in, 1, writer); // Print ASCII character
|
||||
} else {
|
||||
snprintf(escape, sizeof(escape), "\\u%04X", c);
|
||||
len += sink(escape, 6, writer); // ASCII control character
|
||||
}
|
||||
continue;
|
||||
} else if ((in & 0xE0) == 0xC0) { // Starts with `110'
|
||||
size = 2;
|
||||
c = in & 0x1F;
|
||||
} else if ((in & 0xF0) == 0xE0) { // Starts with `1110'
|
||||
size = 3;
|
||||
c = in & 0x0F;
|
||||
} else if ((in & 0xF8) == 0xF0) { // Starts with `11110'
|
||||
size = 4;
|
||||
c = in & 0x07;
|
||||
} else {
|
||||
w_err(writer, SERD_ERR_BAD_ARG, "invalid UTF-8: %X\n", in);
|
||||
const uint8_t replacement_char[] = { 0xEF, 0xBF, 0xBD };
|
||||
len += sink(replacement_char, sizeof(replacement_char), writer);
|
||||
return len;
|
||||
}
|
||||
|
||||
if (ctx != WRITE_URI && !(writer->style & SERD_STYLE_ASCII)) {
|
||||
// Write UTF-8 character directly to UTF-8 output
|
||||
// TODO: Always parse and validate character?
|
||||
len += sink(utf8 + i - 1, size, writer);
|
||||
i += size - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
#define READ_BYTE() \
|
||||
in = utf8[i++] & 0x3f; \
|
||||
c = (c << 6) | in;
|
||||
|
||||
switch (size) {
|
||||
case 4: READ_BYTE();
|
||||
case 3: READ_BYTE();
|
||||
case 2: READ_BYTE();
|
||||
}
|
||||
|
||||
if (c < 0xFFFF) {
|
||||
snprintf(escape, sizeof(escape), "\\u%04X", c);
|
||||
len += sink(escape, 6, writer);
|
||||
} else {
|
||||
snprintf(escape, sizeof(escape), "\\U%08X", c);
|
||||
len += sink(escape, 10, writer);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t
|
||||
uri_sink(const void* buf, size_t len, void* stream)
|
||||
{
|
||||
return write_text((SerdWriter*)stream, WRITE_URI,
|
||||
(const uint8_t*)buf, len);
|
||||
}
|
||||
|
||||
static void
|
||||
write_newline(SerdWriter* writer)
|
||||
{
|
||||
sink("\n", 1, writer);
|
||||
for (unsigned i = 0; i < writer->indent; ++i) {
|
||||
sink("\t", 1, writer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
write_sep(SerdWriter* writer, const Sep sep)
|
||||
{
|
||||
const SepRule* rule = &rules[sep];
|
||||
if (rule->space_before) {
|
||||
write_newline(writer);
|
||||
}
|
||||
if (rule->str) {
|
||||
sink(rule->str, rule->len, writer);
|
||||
}
|
||||
if ( (writer->last_sep && rule->space_after_sep)
|
||||
|| (!writer->last_sep && rule->space_after_node)) {
|
||||
write_newline(writer);
|
||||
} else if (writer->last_sep && rule->space_after_node) {
|
||||
sink(" ", 1, writer);
|
||||
}
|
||||
writer->last_sep = sep;
|
||||
}
|
||||
|
||||
static SerdStatus
|
||||
reset_context(SerdWriter* writer, bool del)
|
||||
{
|
||||
if (del) {
|
||||
serd_node_free(&writer->context.graph);
|
||||
serd_node_free(&writer->context.subject);
|
||||
serd_node_free(&writer->context.predicate);
|
||||
writer->context = WRITE_CONTEXT_NULL;
|
||||
} else {
|
||||
writer->context.graph.type = SERD_NOTHING;
|
||||
writer->context.subject.type = SERD_NOTHING;
|
||||
writer->context.predicate.type = SERD_NOTHING;
|
||||
}
|
||||
writer->empty = false;
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FIELD_NONE,
|
||||
FIELD_SUBJECT,
|
||||
FIELD_PREDICATE,
|
||||
FIELD_OBJECT
|
||||
} Field;
|
||||
|
||||
static bool
|
||||
write_node(SerdWriter* writer,
|
||||
const SerdNode* node,
|
||||
const SerdNode* datatype,
|
||||
const SerdNode* lang,
|
||||
Field field,
|
||||
SerdStatementFlags flags)
|
||||
{
|
||||
SerdChunk uri_prefix;
|
||||
SerdChunk uri_suffix;
|
||||
bool has_scheme;
|
||||
switch (node->type) {
|
||||
case SERD_BLANK:
|
||||
if (writer->syntax != SERD_NTRIPLES
|
||||
&& ((field == FIELD_SUBJECT && (flags & SERD_ANON_S_BEGIN))
|
||||
|| (field == FIELD_OBJECT && (flags & SERD_ANON_O_BEGIN)))) {
|
||||
++writer->indent;
|
||||
write_sep(writer, SEP_ANON_BEGIN);
|
||||
} else if (writer->syntax != SERD_NTRIPLES
|
||||
&& (field == FIELD_SUBJECT && (flags & SERD_LIST_S_BEGIN))) {
|
||||
assert(writer->list_depth == 0);
|
||||
copy_node(&writer->list_subj, node);
|
||||
++writer->list_depth;
|
||||
++writer->indent;
|
||||
write_sep(writer, SEP_LIST_BEGIN);
|
||||
} else if (writer->syntax != SERD_NTRIPLES
|
||||
&& (field == FIELD_OBJECT && (flags & SERD_LIST_O_BEGIN))) {
|
||||
++writer->indent;
|
||||
++writer->list_depth;
|
||||
write_sep(writer, SEP_LIST_BEGIN);
|
||||
} else if (writer->syntax != SERD_NTRIPLES
|
||||
&& ((field == FIELD_SUBJECT && (flags & SERD_EMPTY_S))
|
||||
|| (field == FIELD_OBJECT && (flags & SERD_EMPTY_O)))) {
|
||||
sink("[]", 2, writer);
|
||||
} else {
|
||||
sink("_:", 2, writer);
|
||||
if (writer->bprefix && !strncmp((const char*)node->buf,
|
||||
(const char*)writer->bprefix,
|
||||
writer->bprefix_len)) {
|
||||
sink(node->buf + writer->bprefix_len,
|
||||
node->n_bytes - writer->bprefix_len,
|
||||
writer);
|
||||
} else {
|
||||
sink(node->buf, node->n_bytes, writer);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SERD_CURIE:
|
||||
switch (writer->syntax) {
|
||||
case SERD_NTRIPLES:
|
||||
if (serd_env_expand(writer->env, node, &uri_prefix, &uri_suffix)) {
|
||||
w_err(writer, SERD_ERR_BAD_CURIE,
|
||||
"undefined namespace prefix `%s'\n", node->buf);
|
||||
return false;
|
||||
}
|
||||
sink("<", 1, writer);
|
||||
write_text(writer, WRITE_URI, uri_prefix.buf, uri_prefix.len);
|
||||
write_text(writer, WRITE_URI, uri_suffix.buf, uri_suffix.len);
|
||||
sink(">", 1, writer);
|
||||
break;
|
||||
case SERD_TURTLE:
|
||||
sink(node->buf, node->n_bytes, writer);
|
||||
}
|
||||
break;
|
||||
case SERD_LITERAL:
|
||||
if (writer->syntax == SERD_TURTLE && datatype && datatype->buf) {
|
||||
const char* type_uri = (const char*)datatype->buf;
|
||||
if (!strncmp(type_uri, NS_XSD, sizeof(NS_XSD) - 1) && (
|
||||
!strcmp(type_uri + sizeof(NS_XSD) - 1, "boolean") ||
|
||||
!strcmp(type_uri + sizeof(NS_XSD) - 1, "decimal") ||
|
||||
!strcmp(type_uri + sizeof(NS_XSD) - 1, "integer"))) {
|
||||
sink(node->buf, node->n_bytes, writer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (writer->syntax != SERD_NTRIPLES
|
||||
&& (node->flags & (SERD_HAS_NEWLINE|SERD_HAS_QUOTE))) {
|
||||
sink("\"\"\"", 3, writer);
|
||||
write_text(writer, WRITE_LONG_STRING, node->buf, node->n_bytes);
|
||||
sink("\"\"\"", 3, writer);
|
||||
} else {
|
||||
sink("\"", 1, writer);
|
||||
write_text(writer, WRITE_STRING, node->buf, node->n_bytes);
|
||||
sink("\"", 1, writer);
|
||||
}
|
||||
if (lang && lang->buf) {
|
||||
sink("@", 1, writer);
|
||||
sink(lang->buf, lang->n_bytes, writer);
|
||||
} else if (datatype && datatype->buf) {
|
||||
sink("^^", 2, writer);
|
||||
write_node(writer, datatype, NULL, NULL, FIELD_NONE, flags);
|
||||
}
|
||||
break;
|
||||
case SERD_URI:
|
||||
has_scheme = serd_uri_string_has_scheme(node->buf);
|
||||
if (field == FIELD_PREDICATE && (writer->syntax == SERD_TURTLE)
|
||||
&& !strcmp((const char*)node->buf, NS_RDF "type")) {
|
||||
sink("a", 1, writer);
|
||||
break;
|
||||
} else if ((writer->syntax == SERD_TURTLE)
|
||||
&& !strcmp((const char*)node->buf, NS_RDF "nil")) {
|
||||
sink("()", 2, writer);
|
||||
break;
|
||||
} else if (has_scheme && (writer->style & SERD_STYLE_CURIED)) {
|
||||
SerdNode prefix;
|
||||
SerdChunk suffix;
|
||||
if (serd_env_qualify(writer->env, node, &prefix, &suffix)) {
|
||||
write_text(writer, WRITE_URI, prefix.buf, prefix.n_bytes);
|
||||
sink(":", 1, writer);
|
||||
write_text(writer, WRITE_URI, suffix.buf, suffix.len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sink("<", 1, writer);
|
||||
if (writer->style & SERD_STYLE_RESOLVED) {
|
||||
SerdURI in_base_uri, uri, abs_uri;
|
||||
serd_env_get_base_uri(writer->env, &in_base_uri);
|
||||
serd_uri_parse(node->buf, &uri);
|
||||
serd_uri_resolve(&uri, &in_base_uri, &abs_uri);
|
||||
bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri);
|
||||
SerdURI* root = rooted ? &writer->root_uri : & writer->base_uri;
|
||||
if (!uri_is_under(&abs_uri, root) ||
|
||||
writer->syntax == SERD_NTRIPLES) {
|
||||
serd_uri_serialise(&abs_uri, uri_sink, writer);
|
||||
} else {
|
||||
serd_uri_serialise_relative(
|
||||
&uri, &writer->base_uri, root, uri_sink, writer);
|
||||
}
|
||||
} else {
|
||||
write_text(writer, WRITE_URI, node->buf, node->n_bytes);
|
||||
}
|
||||
sink(">", 1, writer);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
writer->last_sep = SEP_NONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_resource(const SerdNode* node)
|
||||
{
|
||||
return node->type > SERD_LITERAL;
|
||||
}
|
||||
|
||||
static void
|
||||
write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred)
|
||||
{
|
||||
write_node(writer, pred, NULL, NULL, FIELD_PREDICATE, flags);
|
||||
write_sep(writer, SEP_P_O);
|
||||
copy_node(&writer->context.predicate, pred);
|
||||
}
|
||||
|
||||
static bool
|
||||
write_list_obj(SerdWriter* writer,
|
||||
SerdStatementFlags flags,
|
||||
const SerdNode* predicate,
|
||||
const SerdNode* object,
|
||||
const SerdNode* datatype,
|
||||
const SerdNode* lang)
|
||||
{
|
||||
if (!strcmp((const char*)object->buf, NS_RDF "nil")) {
|
||||
--writer->indent;
|
||||
write_sep(writer, SEP_LIST_END);
|
||||
return true;
|
||||
} else if (!strcmp((const char*)predicate->buf, NS_RDF "first")) {
|
||||
write_sep(writer, SEP_LIST_SEP);
|
||||
write_node(writer, object, datatype, lang, FIELD_OBJECT, flags);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_writer_write_statement(SerdWriter* writer,
|
||||
SerdStatementFlags flags,
|
||||
const SerdNode* graph,
|
||||
const SerdNode* subject,
|
||||
const SerdNode* predicate,
|
||||
const SerdNode* object,
|
||||
const SerdNode* datatype,
|
||||
const SerdNode* lang)
|
||||
{
|
||||
if (!subject || !predicate || !object
|
||||
|| !subject->buf || !predicate->buf || !object->buf
|
||||
|| !is_resource(subject) || !is_resource(predicate)) {
|
||||
return SERD_ERR_BAD_ARG;
|
||||
}
|
||||
|
||||
switch (writer->syntax) {
|
||||
case SERD_NTRIPLES:
|
||||
write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags);
|
||||
sink(" ", 1, writer);
|
||||
write_node(writer, predicate, NULL, NULL, FIELD_PREDICATE, flags);
|
||||
sink(" ", 1, writer);
|
||||
if (!write_node(writer, object, datatype, lang, FIELD_OBJECT, flags)) {
|
||||
return SERD_ERR_UNKNOWN;
|
||||
}
|
||||
sink(" .\n", 3, writer);
|
||||
return SERD_SUCCESS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((flags & SERD_LIST_CONT)) {
|
||||
if (write_list_obj(writer, flags, predicate, object, datatype, lang)) {
|
||||
// Reached end of list
|
||||
if (--writer->list_depth == 0 && writer->list_subj.type) {
|
||||
reset_context(writer, true);
|
||||
writer->context.subject = writer->list_subj;
|
||||
writer->list_subj = SERD_NODE_NULL;
|
||||
}
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
} else if (serd_node_equals(subject, &writer->context.subject)) {
|
||||
if (serd_node_equals(predicate, &writer->context.predicate)) {
|
||||
// Abbreviate S P
|
||||
if (!(flags & SERD_ANON_O_BEGIN)) {
|
||||
++writer->indent;
|
||||
}
|
||||
write_sep(writer, SEP_END_O);
|
||||
write_node(writer, object, datatype, lang, FIELD_OBJECT, flags);
|
||||
if (!(flags & SERD_ANON_O_BEGIN)) {
|
||||
--writer->indent;
|
||||
}
|
||||
} else {
|
||||
// Abbreviate S
|
||||
Sep sep = writer->context.predicate.type ? SEP_END_P : SEP_S_P;
|
||||
write_sep(writer, sep);
|
||||
write_pred(writer, flags, predicate);
|
||||
write_node(writer, object, datatype, lang, FIELD_OBJECT, flags);
|
||||
}
|
||||
} else {
|
||||
// No abbreviation
|
||||
if (writer->context.subject.type) {
|
||||
assert(writer->indent > 0);
|
||||
--writer->indent;
|
||||
if (serd_stack_is_empty(&writer->anon_stack)) {
|
||||
write_sep(writer, SEP_END_S);
|
||||
}
|
||||
} else if (!writer->empty) {
|
||||
write_sep(writer, SEP_S_P);
|
||||
}
|
||||
|
||||
if (!(flags & SERD_ANON_CONT)) {
|
||||
write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags);
|
||||
++writer->indent;
|
||||
write_sep(writer, SEP_S_P);
|
||||
} else {
|
||||
++writer->indent;
|
||||
}
|
||||
|
||||
reset_context(writer, true);
|
||||
copy_node(&writer->context.subject, subject);
|
||||
|
||||
if (!(flags & SERD_LIST_S_BEGIN)) {
|
||||
write_pred(writer, flags, predicate);
|
||||
}
|
||||
|
||||
write_node(writer, object, datatype, lang, FIELD_OBJECT, flags);
|
||||
}
|
||||
|
||||
if (flags & (SERD_ANON_S_BEGIN|SERD_ANON_O_BEGIN)) {
|
||||
WriteContext* ctx = (WriteContext*)serd_stack_push(
|
||||
&writer->anon_stack, sizeof(WriteContext));
|
||||
*ctx = writer->context;
|
||||
WriteContext new_context = {
|
||||
serd_node_copy(graph), serd_node_copy(subject), SERD_NODE_NULL };
|
||||
if ((flags & SERD_ANON_S_BEGIN)) {
|
||||
new_context.predicate = serd_node_copy(predicate);
|
||||
}
|
||||
writer->context = new_context;
|
||||
} else {
|
||||
copy_node(&writer->context.graph, graph);
|
||||
copy_node(&writer->context.subject, subject);
|
||||
copy_node(&writer->context.predicate, predicate);
|
||||
}
|
||||
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_writer_end_anon(SerdWriter* writer,
|
||||
const SerdNode* node)
|
||||
{
|
||||
if (writer->syntax == SERD_NTRIPLES) {
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
if (serd_stack_is_empty(&writer->anon_stack)) {
|
||||
w_err(writer, SERD_ERR_UNKNOWN,
|
||||
"unexpected end of anonymous node\n");
|
||||
return SERD_ERR_UNKNOWN;
|
||||
}
|
||||
assert(writer->indent > 0);
|
||||
--writer->indent;
|
||||
write_sep(writer, SEP_ANON_END);
|
||||
reset_context(writer, true);
|
||||
writer->context = *anon_stack_top(writer);
|
||||
serd_stack_pop(&writer->anon_stack, sizeof(WriteContext));
|
||||
const bool is_subject = serd_node_equals(node, &writer->context.subject);
|
||||
if (is_subject) {
|
||||
copy_node(&writer->context.subject, node);
|
||||
writer->context.predicate.type = SERD_NOTHING;
|
||||
}
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_writer_finish(SerdWriter* writer)
|
||||
{
|
||||
if (writer->context.subject.type) {
|
||||
sink(" .\n", 3, writer);
|
||||
}
|
||||
if (writer->style & SERD_STYLE_BULK) {
|
||||
serd_bulk_sink_flush(&writer->bulk_sink);
|
||||
}
|
||||
writer->indent = 0;
|
||||
return reset_context(writer, true);
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdWriter*
|
||||
serd_writer_new(SerdSyntax syntax,
|
||||
SerdStyle style,
|
||||
SerdEnv* env,
|
||||
const SerdURI* base_uri,
|
||||
SerdSink ssink,
|
||||
void* stream)
|
||||
{
|
||||
const WriteContext context = WRITE_CONTEXT_NULL;
|
||||
SerdWriter* writer = (SerdWriter*)malloc(sizeof(SerdWriter));
|
||||
writer->syntax = syntax;
|
||||
writer->style = style;
|
||||
writer->env = env;
|
||||
writer->root_node = SERD_NODE_NULL;
|
||||
writer->root_uri = SERD_URI_NULL;
|
||||
writer->base_uri = base_uri ? *base_uri : SERD_URI_NULL;
|
||||
writer->anon_stack = serd_stack_new(sizeof(WriteContext));
|
||||
writer->sink = ssink;
|
||||
writer->stream = stream;
|
||||
writer->error_sink = NULL;
|
||||
writer->error_handle = NULL;
|
||||
writer->context = context;
|
||||
writer->list_subj = SERD_NODE_NULL;
|
||||
writer->list_depth = 0;
|
||||
writer->bprefix = NULL;
|
||||
writer->bprefix_len = 0;
|
||||
writer->indent = 0;
|
||||
writer->last_sep = SEP_NONE;
|
||||
writer->empty = true;
|
||||
if (style & SERD_STYLE_BULK) {
|
||||
writer->bulk_sink = serd_bulk_sink_new(ssink, stream, SERD_PAGE_SIZE);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void
|
||||
serd_writer_set_error_sink(SerdWriter* writer,
|
||||
SerdErrorSink error_sink,
|
||||
void* error_handle)
|
||||
{
|
||||
writer->error_sink = error_sink;
|
||||
writer->error_handle = error_handle;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void
|
||||
serd_writer_chop_blank_prefix(SerdWriter* writer,
|
||||
const uint8_t* prefix)
|
||||
{
|
||||
free(writer->bprefix);
|
||||
writer->bprefix_len = 0;
|
||||
writer->bprefix = NULL;
|
||||
if (prefix) {
|
||||
writer->bprefix_len = strlen((const char*)prefix);
|
||||
writer->bprefix = (uint8_t*)malloc(writer->bprefix_len + 1);
|
||||
memcpy(writer->bprefix, prefix, writer->bprefix_len + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_writer_set_base_uri(SerdWriter* writer,
|
||||
const SerdNode* uri)
|
||||
{
|
||||
if (!serd_env_set_base_uri(writer->env, uri)) {
|
||||
serd_env_get_base_uri(writer->env, &writer->base_uri);
|
||||
|
||||
if (writer->syntax != SERD_NTRIPLES) {
|
||||
if (writer->context.graph.type || writer->context.subject.type) {
|
||||
sink(" .\n\n", 4, writer);
|
||||
reset_context(writer, false);
|
||||
}
|
||||
sink("@base <", 7, writer);
|
||||
sink(uri->buf, uri->n_bytes, writer);
|
||||
sink("> .\n", 4, writer);
|
||||
}
|
||||
writer->indent = 0;
|
||||
return reset_context(writer, false);
|
||||
}
|
||||
return SERD_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_writer_set_root_uri(SerdWriter* writer,
|
||||
const SerdNode* uri)
|
||||
{
|
||||
serd_node_free(&writer->root_node);
|
||||
if (uri && uri->buf) {
|
||||
writer->root_node = serd_node_copy(uri);
|
||||
serd_uri_parse(uri->buf, &writer->root_uri);
|
||||
} else {
|
||||
writer->root_node = SERD_NODE_NULL;
|
||||
writer->root_uri = SERD_URI_NULL;
|
||||
}
|
||||
return SERD_SUCCESS;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdStatus
|
||||
serd_writer_set_prefix(SerdWriter* writer,
|
||||
const SerdNode* name,
|
||||
const SerdNode* uri)
|
||||
{
|
||||
if (!serd_env_set_prefix(writer->env, name, uri)) {
|
||||
if (writer->syntax != SERD_NTRIPLES) {
|
||||
if (writer->context.graph.type || writer->context.subject.type) {
|
||||
sink(" .\n\n", 4, writer);
|
||||
reset_context(writer, false);
|
||||
}
|
||||
sink("@prefix ", 8, writer);
|
||||
sink(name->buf, name->n_bytes, writer);
|
||||
sink(": <", 3, writer);
|
||||
write_text(writer, WRITE_URI, uri->buf, uri->n_bytes);
|
||||
sink("> .\n", 4, writer);
|
||||
}
|
||||
writer->indent = 0;
|
||||
return reset_context(writer, false);
|
||||
}
|
||||
return SERD_ERR_UNKNOWN;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
void
|
||||
serd_writer_free(SerdWriter* writer)
|
||||
{
|
||||
serd_writer_finish(writer);
|
||||
serd_stack_free(&writer->anon_stack);
|
||||
free(writer->bprefix);
|
||||
if (writer->style & SERD_STYLE_BULK) {
|
||||
serd_bulk_sink_free(&writer->bulk_sink);
|
||||
}
|
||||
serd_node_free(&writer->root_node);
|
||||
free(writer);
|
||||
}
|
||||
|
||||
SERD_API
|
||||
SerdEnv*
|
||||
serd_writer_get_env(SerdWriter* writer)
|
||||
{
|
||||
return writer->env;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
size_t
|
||||
serd_file_sink(const void* buf, size_t len, void* stream)
|
||||
{
|
||||
return fwrite(buf, 1, len, (FILE*)stream);
|
||||
}
|
||||
|
||||
SERD_API
|
||||
size_t
|
||||
serd_chunk_sink(const void* buf, size_t len, void* stream)
|
||||
{
|
||||
SerdChunk* chunk = (SerdChunk*)stream;
|
||||
chunk->buf = (uint8_t*)realloc((uint8_t*)chunk->buf, chunk->len + len);
|
||||
memcpy((uint8_t*)chunk->buf + chunk->len, buf, len);
|
||||
chunk->len += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
SERD_API
|
||||
uint8_t*
|
||||
serd_chunk_sink_finish(SerdChunk* stream)
|
||||
{
|
||||
serd_chunk_sink("", 1, stream);
|
||||
return (uint8_t*)stream->buf;
|
||||
}
|
||||
Reference in New Issue
Block a user