1
0
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:
lllucius
2013-10-27 08:56:51 +00:00
parent 52afa22aaf
commit bc5e7ffce8
588 changed files with 170918 additions and 0 deletions

271
lib-src/lv2/serd/src/env.c Normal file
View 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
View 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;
}
}

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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
View 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);
}

View 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;
}