@@ -104,3 +104,6 @@ c++/carla-native/zynaddsubfx/UI/SUBnoteUI.cc | |||
c++/carla-native/zynaddsubfx/UI/SUBnoteUI.h | |||
c++/carla-native/zynaddsubfx/UI/VirKeyboard.cc | |||
c++/carla-native/zynaddsubfx/UI/VirKeyboard.h | |||
# Other | |||
libs/jackbridge/jack/ |
@@ -7,5 +7,5 @@ else | |||
fi | |||
INSTALL_PREFIX="X-PREFIX-X" | |||
export PATH="$INSTALL_PREFIX"/lib/cadence:$PATH | |||
exec $PYTHON $INSTALL_PREFIX/share/cadence/src/carla.py --with-libprefix="$INSTALL_PREFIX" "$@" | |||
export PATH="$INSTALL_PREFIX"/lib/carla:$PATH | |||
exec $PYTHON $INSTALL_PREFIX/share/carla/carla.py --with-libprefix="$INSTALL_PREFIX" "$@" |
@@ -7,4 +7,4 @@ else | |||
fi | |||
INSTALL_PREFIX="X-PREFIX-X" | |||
exec $PYTHON $INSTALL_PREFIX/share/cadence/src/carla_control.py "$@" | |||
exec $PYTHON $INSTALL_PREFIX/share/carla/carla_control.py "$@" |
@@ -2,7 +2,7 @@ | |||
# Script to start Carla bridges | |||
INSTALL_PREFIX="X-PREFIX-X" | |||
CADENCE_PREFIX="$INSTALL_PREFIX"/lib/cadence | |||
CARLA_PREFIX="$INSTALL_PREFIX"/lib/carla | |||
# ---------------------------------------------------------------------- | |||
# Check for enough arguments | |||
@@ -58,17 +58,17 @@ if [ $RUN_ARCH == "win64" ]; then | |||
fi | |||
# ---------------------------------------------------------------------- | |||
# Check for existing cadence folder | |||
# Check for existing carla folder | |||
if [ ! -d $CADENCE_PREFIX ]; then | |||
echo "$0: Cadence folder non-existing, is it installed?" | |||
if [ ! -d $CARLA_PREFIX ]; then | |||
echo "$0: Carla folder does not exist, is it installed?" | |||
exit | |||
fi | |||
# ---------------------------------------------------------------------- | |||
# Check for existing arch binary | |||
CARLA_EXEC="$CADENCE_PREFIX/carla-bridge-$RUN_ARCH" | |||
CARLA_EXEC="$CARLA_PREFIX/carla-bridge-$RUN_ARCH" | |||
if [ ! -f $CARLA_EXEC ]; then | |||
echo "$0: Invalid arch (may not be installed)" | |||
@@ -0,0 +1,62 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for carla libs # | |||
# ----------------------------------------- # | |||
# Created by falkTX | |||
# | |||
all: | |||
# -------------------------------------------------------------- | |||
jackbridge-win32.dll: | |||
$(MAKE) -C jackbridge win32 | |||
jackbridge-win64.dll: | |||
$(MAKE) -C jackbridge win64 | |||
jackbridge-win32.dll.so: | |||
$(MAKE) -C jackbridge wine32 | |||
jackbridge-win64.dll.so: | |||
$(MAKE) -C jackbridge wine64 | |||
# -------------------------------------------------------------- | |||
lilv.a: | |||
$(MAKE) -C lilv | |||
lilv_posix32.a: | |||
$(MAKE) -C lilv posix32 | |||
lilv_posix64.a: | |||
$(MAKE) -C lilv posix64 | |||
lilv_win32.a: | |||
$(MAKE) -C lilv win32 | |||
lilv_win64.a: | |||
$(MAKE) -C lilv win64 | |||
# -------------------------------------------------------------- | |||
rtmempool.a: | |||
$(MAKE) -C rtmempool | |||
rtmempool_posix32.a: | |||
$(MAKE) -C rtmempool posix32 | |||
rtmempool_posix64.a: | |||
$(MAKE) -C rtmempool posix64 | |||
rtmempool_win32.a: | |||
$(MAKE) -C rtmempool win32 | |||
rtmempool_win64.a: | |||
$(MAKE) -C rtmempool win64 | |||
# -------------------------------------------------------------- | |||
clean: | |||
rm -f *.a *.def *.dll *.so | |||
$(MAKE) clean -C lilv | |||
$(MAKE) clean -C rtmempool |
@@ -0,0 +1,51 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for jackbridge # | |||
# ------------------------------------- # | |||
# Created by falkTX | |||
# | |||
include ../../Makefile.mk | |||
# -------------------------------------------------------------- | |||
WINECC ?= winegcc | |||
BUILD_C_FLAGS += -DJACKBRIDGE_EXPORT -I. | |||
LINK_FLAGS += -shared | |||
WIN_BUILD_FLAGS = $(BUILD_C_FLAGS) -DJACKBRIDGE_DUMMY -w | |||
WIN_32BIT_FLAGS = $(32BIT_FLAGS) | |||
WIN_64BIT_FLAGS = $(64BIT_FLAGS) | |||
WIN_LINK_FLAGS = $(LINK_FLAGS) | |||
WINE_BUILD_FLAGS = $(BUILD_C_FLAGS) -fPIC | |||
WINE_32BIT_FLAGS = $(32BIT_FLAGS) -L/usr/lib32/wine -L/usr/lib/i386-linux-gnu/wine | |||
WINE_64BIT_FLAGS = $(64BIT_FLAGS) -L/usr/lib64/wine -L/usr/lib/x86_64-linux-gnu/wine | |||
WINE_LINK_FLAGS = $(LINK_FLAGS) $(shell pkg-config --libs jack) -ldl | |||
OBJS = jackbridge.c | |||
# -------------------------------------------------------------- | |||
all: | |||
win32: ../jackbridge-win32.dll | |||
win64: ../jackbridge-win64.dll | |||
wine32: ../jackbridge-win32.dll.so | |||
wine64: ../jackbridge-win64.dll.so | |||
# -------------------------------------------------------------- | |||
../jackbridge-win32.dll: $(OBJS) | |||
$(CC) $^ $(WIN_BUILD_FLAGS) $(WIN_32BIT_FLAGS) $(WIN_LINK_FLAGS) -Wl,--output-def,$@.def,--out-implib,$@.a -o $@ && $(STRIP) $@ | |||
../jackbridge-win64.dll: $(OBJS) | |||
$(CC) $^ $(WIN_BUILD_FLAGS) $(WIN_64BIT_FLAGS) $(WIN_LINK_FLAGS) -Wl,--output-def,$@.def,--out-implib,$@.a -o $@ && $(STRIP) $@ | |||
../jackbridge-win32.dll.so: $(OBJS) ../jackbridge-win32.dll.def | |||
$(WINECC) $^ $(WINE_BUILD_FLAGS) $(WINE_32BIT_FLAGS) $(WINE_LINK_FLAGS) -mno-cygwin -o $@ && $(STRIP) $@ | |||
../jackbridge-win64.dll.so: $(OBJS) ../jackbridge-win64.dll.def | |||
$(WINECC) $^ $(WINE_BUILD_FLAGS) $(WINE_64BIT_FLAGS) $(WINE_LINK_FLAGS) -mno-cygwin -o $@ && $(STRIP) $@ | |||
# -------------------------------------------------------------- |
@@ -0,0 +1,259 @@ | |||
/* | |||
* JackBridge | |||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the COPYING file | |||
*/ | |||
#include "jackbridge.h" | |||
// ----------------------------------------------------------------------------- | |||
jack_client_t* jackbridge_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_client_open(client_name, options, status); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_client_close(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_client_close(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_client_name_size() | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_client_name_size(); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
char* jackbridge_get_client_name(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_get_client_name(client); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_port_name_size() | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_name_size(); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_recompute_total_latencies(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_recompute_total_latencies(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void jackbridge_port_get_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_port_get_latency_range(port, mode, range); | |||
#endif | |||
} | |||
void jackbridge_port_set_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_port_set_latency_range(port, mode, range); | |||
#endif | |||
} | |||
int jackbridge_activate(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_activate(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_deactivate(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_deactivate(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_on_shutdown(client, shutdown_callback, arg); | |||
#endif | |||
} | |||
int jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback latency_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_latency_callback(client, latency_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_process_callback(client, process_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallback freewheel_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_freewheel_callback(client, freewheel_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback bufsize_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_buffer_size_callback(client, bufsize_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback srate_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_sample_rate_callback(client, srate_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_get_sample_rate(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_get_buffer_size(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_register(client, port_name, port_type, flags, buffer_size); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_port_unregister(jack_client_t* client, jack_port_t* port) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_unregister(client, port); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_get_buffer(port, nframes); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------------- | |||
uint32_t jackbridge_midi_get_event_count(void* port_buffer) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_get_event_count(port_buffer); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_event_get(event, port_buffer, event_index); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void jackbridge_midi_clear_buffer(void* port_buffer) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_midi_clear_buffer(port_buffer); | |||
#endif | |||
} | |||
jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_event_reserve(port_buffer, time, data_size); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_event_write(port_buffer, time, data, data_size); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------------- | |||
jack_transport_state_t jackbridge_transport_query(const jack_client_t* client, jack_position_t* pos) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_transport_query(client, pos); | |||
#else | |||
return JackTransportStopped; | |||
#endif | |||
} |
@@ -0,0 +1,111 @@ | |||
/* | |||
* JackBridge | |||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the COPYING file | |||
*/ | |||
#ifndef __JACKBRIDGE_H__ | |||
#define __JACKBRIDGE_H__ | |||
#if defined(_WIN32) && ! defined(__WINE__) | |||
# include <stdint.h> | |||
# include <windows.h> | |||
# define BRIDGE_EXPORT __declspec (dllexport) | |||
#else | |||
# define BRIDGE_EXPORT __attribute__ ((visibility("default"))) | |||
#endif | |||
#ifdef __WINE__ | |||
# define GNU_WIN32 // fix jack_native_thread_t | |||
#endif | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#include <jack/transport.h> | |||
#ifdef JACKBRIDGE_EXPORT | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
BRIDGE_EXPORT jack_client_t* jackbridge_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...); | |||
BRIDGE_EXPORT int jackbridge_client_close(jack_client_t* client); | |||
BRIDGE_EXPORT int jackbridge_client_name_size(); | |||
BRIDGE_EXPORT char* jackbridge_get_client_name(jack_client_t* client); | |||
BRIDGE_EXPORT int jackbridge_port_name_size(); | |||
BRIDGE_EXPORT int jackbridge_recompute_total_latencies(jack_client_t* client); | |||
BRIDGE_EXPORT void jackbridge_port_get_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range); | |||
BRIDGE_EXPORT void jackbridge_port_set_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range); | |||
BRIDGE_EXPORT int jackbridge_activate(jack_client_t* client); | |||
BRIDGE_EXPORT int jackbridge_deactivate(jack_client_t* client); | |||
BRIDGE_EXPORT void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback latency_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallback freewheel_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback bufsize_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback srate_callback, void* arg); | |||
BRIDGE_EXPORT jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client); | |||
BRIDGE_EXPORT jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client); | |||
BRIDGE_EXPORT jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); | |||
BRIDGE_EXPORT int jackbridge_port_unregister(jack_client_t* client, jack_port_t* port); | |||
BRIDGE_EXPORT void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes); | |||
BRIDGE_EXPORT uint32_t jackbridge_midi_get_event_count(void* port_buffer); | |||
BRIDGE_EXPORT int jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index); | |||
BRIDGE_EXPORT void jackbridge_midi_clear_buffer(void* port_buffer); | |||
BRIDGE_EXPORT jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size); | |||
BRIDGE_EXPORT int jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size); | |||
BRIDGE_EXPORT jack_transport_state_t jackbridge_transport_query(const jack_client_t* client, jack_position_t* pos); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#else // JACKBRIDGE_EXPORT | |||
#define jackbridge_client_open jack_client_open | |||
#define jackbridge_client_close jack_client_close | |||
#define jackbridge_client_name_size jack_client_name_size | |||
#define jackbridge_get_client_name jack_get_client_name | |||
#define jackbridge_port_name_size jack_port_name_size | |||
#define jackbridge_recompute_total_latencies jack_recompute_total_latencies | |||
#define jackbridge_port_get_latency_range jack_port_get_latency_range | |||
#define jackbridge_port_set_latency_range jack_port_set_latency_range | |||
#define jackbridge_activate jack_activate | |||
#define jackbridge_deactivate jack_deactivate | |||
#define jackbridge_on_shutdown jack_on_shutdown | |||
#define jackbridge_set_latency_callback jack_set_latency_callback | |||
#define jackbridge_set_process_callback jack_set_process_callback | |||
#define jackbridge_set_freewheel_callback jack_set_freewheel_callback | |||
#define jackbridge_set_buffer_size_callback jack_set_buffer_size_callback | |||
#define jackbridge_set_sample_rate_callback jack_set_sample_rate_callback | |||
#define jackbridge_get_sample_rate jack_get_sample_rate | |||
#define jackbridge_get_buffer_size jack_get_buffer_size | |||
#define jackbridge_port_register jack_port_register | |||
#define jackbridge_port_unregister jack_port_unregister | |||
#define jackbridge_port_get_buffer jack_port_get_buffer | |||
#define jackbridge_midi_get_event_count jack_midi_get_event_count | |||
#define jackbridge_midi_event_get jack_midi_event_get | |||
#define jackbridge_midi_clear_buffer jack_midi_clear_buffer | |||
#define jackbridge_midi_event_reserve jack_midi_event_reserve | |||
#define jackbridge_midi_event_write jack_midi_event_write | |||
#define jackbridge_transport_query jack_transport_query | |||
#endif // JACKBRIDGE_EXPORT | |||
#endif // __JACKBRIDGE_H__ |
@@ -0,0 +1,96 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for lilv # | |||
# ----------------------- # | |||
# Created by falkTX | |||
# | |||
include ../../Makefile.mk | |||
# -------------------------------------------------------------- | |||
SERD_VERSION = 0.18.2 | |||
SORD_VERSION = 0.10.4 | |||
SRATOM_VERSION = 0.4.0 | |||
LILV_VERSION = 0.14.4 | |||
BUILD_C_FLAGS += -fvisibility=hidden -fPIC -w | |||
BUILD_C_FLAGS += -Iconfig -I../../includes | |||
OBJS = serd.o sord.o sratom.o lilv.o | |||
OBJS_posix32 = serd_posix32.o sord_posix32.o sratom_posix32.o lilv_posix32.o | |||
OBJS_posix64 = serd_posix64.o sord_posix64.o sratom_posix64.o lilv_posix64.o | |||
OBJS_win32 = serd_win32.o sord_win32.o sratom_win32.o lilv_win32.o | |||
OBJS_win64 = serd_win64.o sord_win64.o sratom_win64.o lilv_win64.o | |||
# -------------------------------------------------------------- | |||
all: ../lilv.a | |||
posix32: ../lilv_posix32.a | |||
posix64: ../lilv_posix64.a | |||
win32: ../lilv_win32.a | |||
win64: ../lilv_win64.a | |||
# -------------------------------------------------------------- | |||
../lilv.a: $(OBJS) | |||
$(AR) rs $@ $^ | |||
../lilv_posix32.a: $(OBJS_posix32) | |||
$(AR) rs $@ $^ | |||
../lilv_posix64.a: $(OBJS_posix64) | |||
$(AR) rs $@ $^ | |||
../lilv_win32.a: $(OBJS_win32) | |||
$(AR) rs $@ $^ | |||
../lilv_win64.a: $(OBJS_win64) | |||
$(AR) rs $@ $^ | |||
# -------------------------------------------------------------- | |||
serd.o: serd.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) -c -o $@ | |||
sord.o: sord.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src -c -o $@ | |||
sratom.o: sratom.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) -c -o $@ | |||
lilv.o: lilv.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src -c -o $@ | |||
# -------------------------------------------------------------- | |||
serd_%32.o: serd.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) $(32BIT_FLAGS) -c -o $@ | |||
sord_%32.o: sord.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src $(32BIT_FLAGS) -c -o $@ | |||
sratom_%32.o: sratom.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) $(32BIT_FLAGS) -c -o $@ | |||
lilv_%32.o: lilv.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src $(32BIT_FLAGS) -c -o $@ | |||
# -------------------------------------------------------------- | |||
serd_%64.o: serd.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) $(64BIT_FLAGS) -c -o $@ | |||
sord_%64.o: sord.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src $(64BIT_FLAGS) -c -o $@ | |||
sratom_%64.o: sratom.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) $(64BIT_FLAGS) -c -o $@ | |||
lilv_%64.o: lilv.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src $(64BIT_FLAGS) -c -o $@ | |||
# -------------------------------------------------------------- | |||
clean: | |||
rm -f *.o |
@@ -0,0 +1,35 @@ | |||
#ifndef _LILV_CONFIG_H_ | |||
#define _LILV_CONFIG_H_ | |||
#define LILV_VERSION "0.14.4" | |||
#define LILV_NEW_LV2 1 | |||
#define HAVE_LV2 1 | |||
#define HAVE_SERD 1 | |||
#define HAVE_SORD 1 | |||
#define HAVE_SRATOM 1 | |||
#define HAVE_FILENO 1 | |||
#define HAVE_CLOCK_GETTIME 1 | |||
#ifdef __WIN32__ | |||
#define LILV_PATH_SEP ";" | |||
#define LILV_DIR_SEP "\\" | |||
#else | |||
#define LILV_PATH_SEP ":" | |||
#define LILV_DIR_SEP "/" | |||
#define HAVE_FLOCK 1 | |||
#endif | |||
#if defined(__APPLE__) | |||
#define LILV_DEFAULT_LV2_PATH "~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2:/Library/Audio/Plug-Ins/LV2" | |||
#elif defined(__HAIKU__) | |||
#define LILV_DEFAULT_LV2_PATH "~/.lv2:/boot/common/add-ons/lv2" | |||
#elif defined(__WIN32__) | |||
#define LILV_DEFAULT_LV2_PATH "%APPDATA%\\LV2;%COMMONPROGRAMFILES%\\LV2" | |||
#else | |||
#define LILV_DEFAULT_LV2_PATH "~/.lv2:/usr/lib/lv2:/usr/local/lib/lv2" | |||
#endif | |||
#endif /* _LILV_CONFIG_H_ */ |
@@ -0,0 +1,21 @@ | |||
#ifndef _SERD_CONFIG_H_ | |||
#define _SERD_CONFIG_H_ | |||
#define SERD_VERSION "0.18.2" | |||
#if defined(__APPLE__) || defined(__HAIKU__) | |||
#define HAVE_FMAX 1 | |||
#define HAVE_FILENO 1 | |||
#define HAVE_POSIX_MEMALIGN 1 | |||
#elif defined(__WIN32__) | |||
#define HAVE_FMAX 1 | |||
#define HAVE_FILENO 1 | |||
#else | |||
#define HAVE_FMAX 1 | |||
#define HAVE_FILENO 1 | |||
#define HAVE_POSIX_MEMALIGN 1 | |||
#define HAVE_POSIX_FADVISE 1 | |||
#endif | |||
#endif /* _SERD_CONFIG_H_ */ |
@@ -0,0 +1,10 @@ | |||
#ifndef _SORD_CONFIG_H_ | |||
#define _SORD_CONFIG_H_ | |||
#define SORD_VERSION "0.10.4" | |||
#define HAVE_SERD 1 | |||
#define HAVE_PCRE 1 | |||
#endif /* _SORD_CONFIG_H_ */ |
@@ -0,0 +1,11 @@ | |||
#ifndef _SRATOM_CONFIG_H_ | |||
#define _SRATOM_CONFIG_H_ | |||
#define SRATOM_VERSION "0.4.0" | |||
#define HAVE_LV2 1 | |||
#define HAVE_SERD 1 | |||
#define HAVE_SORD 1 | |||
#endif /* _SRATOM_CONFIG_H_ */ |
@@ -0,0 +1,20 @@ | |||
diff -U 3 -H -b -B -d -r -N -- lilv-0.14.4.old/src/util.c lilv-0.14.4/src/util.c | |||
--- lilv-0.14.4.old/src/util.c 2012-08-09 21:51:00.000000000 +0100 | |||
+++ lilv-0.14.4/src/util.c 2012-09-15 01:20:07.908701939 +0100 | |||
@@ -29,7 +29,6 @@ | |||
#include <string.h> | |||
#ifdef _WIN32 | |||
-# define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */ | |||
# include <windows.h> | |||
# include <direct.h> | |||
# include <io.h> | |||
@@ -426,7 +425,7 @@ | |||
int ret = 0; | |||
if (strcmp(oldpath, newpath)) { | |||
#ifdef _WIN32 | |||
- ret = !CreateSymbolicLink(newpath, oldpath, 0); | |||
+ ret = 0; | |||
#else | |||
ret = symlink(oldpath, newpath); | |||
#endif |
@@ -0,0 +1,137 @@ | |||
diff -U 3 -H -d -r -N -- lilv-0.14.4.old/lilv/lilv.h lilv-0.14.4/lilv/lilv.h | |||
--- lilv-0.14.4.old/lilv/lilv.h 2012-09-07 19:00:48.464571333 +0100 | |||
+++ lilv-0.14.4/lilv/lilv.h 2012-09-07 18:54:00.626548936 +0100 | |||
@@ -1693,6 +1693,25 @@ | |||
lilv_ui_get_binary_uri(const LilvUI* ui); | |||
/** | |||
+ Custom calls | |||
+*/ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_supported_features(const LilvUI* ui); | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_required_features(const LilvUI* ui); | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_optional_features(const LilvUI* ui); | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_extension_data(const LilvUI* ui); | |||
+ | |||
+/** | |||
@} | |||
@} | |||
*/ | |||
diff -U 3 -H -d -r -N -- lilv-0.14.4.old/lilv/lilvmm.hpp lilv-0.14.4/lilv/lilvmm.hpp | |||
--- lilv-0.14.4.old/lilv/lilvmm.hpp 2012-07-18 02:42:43.000000000 +0100 | |||
+++ lilv-0.14.4/lilv/lilvmm.hpp 2012-09-07 18:53:14.134318379 +0100 | |||
@@ -136,6 +136,7 @@ | |||
struct Nodes { | |||
LILV_WRAP_COLL(Nodes, Node, nodes); | |||
LILV_WRAP1(bool, nodes, contains, const Node, node); | |||
+ LILV_WRAP0(Node, nodes, get_first); | |||
}; | |||
struct Port { | |||
@@ -167,6 +168,26 @@ | |||
const LilvPort* me; | |||
}; | |||
+struct UI { | |||
+ inline UI(const LilvUI* c_obj) : me(c_obj) {} | |||
+ LILV_WRAP_CONVERSION(const LilvUI); | |||
+ | |||
+ LILV_WRAP0(Node, ui, get_uri); | |||
+ LILV_WRAP1(bool, ui, is_a, LilvNode*, ui_class); | |||
+ LILV_WRAP0(Node, ui, get_bundle_uri); | |||
+ LILV_WRAP0(Node, ui, get_binary_uri); | |||
+ LILV_WRAP0(Nodes, ui, get_supported_features); | |||
+ LILV_WRAP0(Nodes, ui, get_required_features); | |||
+ LILV_WRAP0(Nodes, ui, get_optional_features); | |||
+ LILV_WRAP0(Nodes, ui, get_extension_data); | |||
+ | |||
+ const LilvUI* me; | |||
+}; | |||
+ | |||
+struct UIs { | |||
+ LILV_WRAP_COLL(UIs, UI, uis); | |||
+}; | |||
+ | |||
struct Plugin { | |||
inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvPlugin); | |||
@@ -190,6 +211,8 @@ | |||
LILV_WRAP0(Node, plugin, get_author_email); | |||
LILV_WRAP0(Node, plugin, get_author_homepage); | |||
LILV_WRAP0(bool, plugin, is_replaced); | |||
+ LILV_WRAP0(Nodes, plugin, get_extension_data); | |||
+ LILV_WRAP0(UIs, plugin, get_uis); | |||
inline Port get_port_by_index(unsigned index) { | |||
return Port(me, lilv_plugin_get_port_by_index(me, index)); | |||
diff -U 3 -H -d -r -N -- lilv-0.14.4.old/src/ui.c lilv-0.14.4/src/ui.c | |||
--- lilv-0.14.4.old/src/ui.c 2012-09-07 19:00:48.464571333 +0100 | |||
+++ lilv-0.14.4/src/ui.c 2012-09-07 18:59:51.652289664 +0100 | |||
@@ -128,3 +128,57 @@ | |||
assert(ui->binary_uri); | |||
return ui->binary_uri; | |||
} | |||
+ | |||
+static LilvNodes* | |||
+lilv_ui_get_value_internal(const LilvUI* ui, | |||
+ const SordNode* predicate) | |||
+{ | |||
+ return lilv_world_query_values_internal( | |||
+ ui->world, ui->uri->node, predicate, NULL); | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_supported_features(const LilvUI* ui) | |||
+{ | |||
+ LilvNodes* optional = lilv_ui_get_optional_features(ui); | |||
+ LilvNodes* required = lilv_ui_get_required_features(ui); | |||
+ LilvNodes* result = lilv_nodes_new(); | |||
+ | |||
+ LILV_FOREACH(nodes, i, optional) | |||
+ zix_tree_insert((ZixTree*)result, | |||
+ lilv_node_duplicate(lilv_nodes_get(optional, i)), | |||
+ NULL); | |||
+ LILV_FOREACH(nodes, i, required) | |||
+ zix_tree_insert((ZixTree*)result, | |||
+ lilv_node_duplicate(lilv_nodes_get(required, i)), | |||
+ NULL); | |||
+ | |||
+ lilv_nodes_free(optional); | |||
+ lilv_nodes_free(required); | |||
+ | |||
+ return result; | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_required_features(const LilvUI* ui) | |||
+{ | |||
+ return lilv_ui_get_value_internal( | |||
+ ui, ui->world->uris.lv2_requiredFeature); | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_optional_features(const LilvUI* ui) | |||
+{ | |||
+ return lilv_ui_get_value_internal( | |||
+ ui, ui->world->uris.lv2_optionalFeature); | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_extension_data(const LilvUI* ui) | |||
+{ | |||
+ return lilv_ui_get_value_internal(ui, ui->world->uris.lv2_extensionData); | |||
+} |
@@ -0,0 +1,11 @@ | |||
diff -U 3 -H -b -B -d -r -N -- lilv-0.14.4.old/lilv/lilvmm.hpp lilv-0.14.4/lilv/lilvmm.hpp | |||
--- lilv-0.14.4.old/lilv/lilvmm.hpp 2012-09-13 12:47:55.000000000 +0100 | |||
+++ lilv-0.14.4/lilv/lilvmm.hpp 2012-09-13 12:48:10.950555311 +0100 | |||
@@ -60,6 +60,7 @@ | |||
#endif | |||
struct Node { | |||
+ inline Node(LilvNode* node) : me(node) {} | |||
inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {} | |||
inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {} | |||
@@ -0,0 +1,8 @@ | |||
Author: | |||
David Robillard <d@drobilla.net> | |||
GTK2 GUI and I18N support: | |||
Lars Luthman <lars.luthman@gmail.com> | |||
Dynamic manifest support: | |||
Stefano D'Angelo |
@@ -0,0 +1,13 @@ | |||
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. |
@@ -0,0 +1,59 @@ | |||
Installation Instructions | |||
========================= | |||
Basic Installation | |||
------------------ | |||
Building this software requires only Python. To install with default options: | |||
./waf configure | |||
./waf | |||
./waf install | |||
You may need to become root for the install stage, for example: | |||
sudo ./waf install | |||
Configuration Options | |||
--------------------- | |||
All supported options can be viewed using the command: | |||
./waf --help | |||
Most options only need to be passed during the configure stage, for example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install | |||
Compiler Configuration | |||
---------------------- | |||
Several standard environment variables can be used to control how compilers are | |||
invoked: | |||
* CC: Path to C compiler | |||
* CFLAGS: C compiler options | |||
* CXX: Path to C++ compiler | |||
* CXXFLAGS: C++ compiler options | |||
* CPPFLAGS: C preprocessor options | |||
* LINKFLAGS: Linker options | |||
Installation Directories | |||
------------------------ | |||
The --prefix option (or the PREFIX environment variable) can be used to change | |||
the prefix which all files are installed under. There are also several options | |||
allowing for more fine-tuned control, see the --help output for details. | |||
Packaging | |||
--------- | |||
Everything can be installed to a specific root directory by passing a --destdir | |||
option to the install stage (or setting the DESTDIR environment variable), | |||
which adds a prefix to all install paths. For example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install --destdir=/tmp/package |
@@ -0,0 +1,90 @@ | |||
lilv (0.14.4) stable; | |||
* Deprecate old flawed Lilv::Instance constructors | |||
* Fix documentation for ui_type parameter of lilv_ui_is_supported() | |||
* Fix crash when lv2info is run with an invalid URI argument | |||
* Gracefully handle failure to save plugin state and print error message | |||
* Reduce memory usage (per node) | |||
* Simpler node implementation always backed by a SordNode | |||
* Make all 'zix' symbols private to avoid symbol clashes in static builds | |||
* Add lv2bench utility | |||
* Fix various hyper-strict warnings | |||
* Do not require a C++ compiler to build | |||
* Add option to build utilities as static binaries | |||
* Upgrade to waf 1.7.2 | |||
* lilvmm.hpp: Make Lilv::Instance handle features and failed instantiations | |||
* lilvmm.hpp: Add Lilv::Instance::get_handle() | |||
* lilvmm.hpp: Add Lilv::Instance::get_extension_data() | |||
-- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 01:38:29 -0400 | |||
lilv (0.14.2) stable; | |||
* Fix dynmanifest support | |||
-- David Robillard <d@drobilla.net> Thu, 19 Apr 2012 16:11:31 -0400 | |||
lilv (0.14.0) stable; | |||
* Add lilv_plugin_get_extension_data | |||
* Use path variables in pkgconfig files | |||
* Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||
* Make Lilv::uri_to_path static inline (fix linking errors) | |||
* Use correct URI for dcterms:replaces (for hiding old plugins): | |||
"http://purl.org/dc/terms/replaces" | |||
* Fix compilation on BSD | |||
* Only load dynmanifest libraries once per bundle, not once per plugin | |||
* Fix lilv_world_find_nodes to work with wildcard subjects | |||
* Add lilv_plugin_get_related to get resources related to plugins that | |||
are not directly rdfs:seeAlso linked (e.g. presets) | |||
* Add lilv_world_load_resource for related resources (e.g. presets) | |||
* Print presets in lv2info | |||
* Remove locale smashing kludges and use new serd functions for converting | |||
nodes to/from numbers. | |||
* Add LilvState API for handling plugin state. This makes it simple to | |||
save and restore plugin state both in memory and on disk, as well as | |||
save presets in a host-sharable way since the disk format is identical | |||
to the LV2 presets format. | |||
* Update old references to lv2_list (now lv2ls) | |||
* Support compilation as C++ under MSVC++. | |||
* Remove use of wordexp. | |||
* Add lilv_plugin_get_port_by_designation() and lilv_port_get_index() as an | |||
improved generic alternative to lilv_plugin_get_latency_port_index(). | |||
* Add lilv_plugin_get_project() and get author information from project if | |||
it is not given directly on the plugin. | |||
-- David Robillard <d@drobilla.net> Wed, 18 Apr 2012 20:06:28 -0400 | |||
lilv (0.5.0) stable; | |||
* Remove glib dependency | |||
* Add lv2core as a pkg-config dependency (for lv2.h header include) | |||
* Obey prefix when installing bash completion script | |||
* Support integer minimum, maximum, and default port values in | |||
lilv_plugin_get_port_ranges_float | |||
* Add ability to build static library | |||
-- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||
lilv (0.4.4) stable; | |||
* Fix building python bindings | |||
* Make free methods tolerate being called on NULL | |||
* Remove lv2jack (replaced by jalv) | |||
* Fix parsing extra plugin data files in other bundles | |||
* Fix lilv_ui_is_supported when Lilv is built independently | |||
-- David Robillard <d@drobilla.net> Sat, 11 Jun 2011 11:20:11 -0400 | |||
lilv (0.4.2) stable; | |||
* Fix compilation issues on some systems | |||
* Fix build system Python 3 compatibility | |||
-- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||
lilv (0.4.0) stable; | |||
* Initial version (forked from SLV2) | |||
-- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 |
@@ -0,0 +1,29 @@ | |||
This library is designed to allow parallel installation of different major | |||
versions. To facilitate this, the shared library name, include directory, and | |||
pkg-config file are suffixed with the major version number of the library. | |||
For example, if this library was named "foo" and at version 1.x.y: | |||
/usr/include/foo-1/foo/foo.h | |||
/usr/lib/foo-1.so.1.x.y | |||
/usr/lib/pkgconfig/foo-1.pc | |||
Dependencies check for pkg-config name "foo-1" and will build | |||
against a compatible version 1, regardless any other installed versions. | |||
*** IMPORTANT GUIDELINES FOR PACKAGERS *** | |||
Packages should follow the same conventions as above, i.e. include the major | |||
version (and only the major version) in the name of the package. Continuing the | |||
example above, the package(s) would be named foo-1 and foo-1-dev. This way, | |||
if/when version 2 comes out, it may be installed at the same time as version 1 | |||
without breaking anything. | |||
Please do not create packages of this library that do not follow these | |||
guidelines, you will break things and cause unnecessary headaches. Please do | |||
not use any number as a suffix other than the actual major version number of the | |||
upstream source package. | |||
Because program and documentation names are not versioned, these should be | |||
included in separate packages which may replace previous versions, since | |||
there is little use in having parallel installations of them. |
@@ -0,0 +1,11 @@ | |||
Lilv | |||
---- | |||
Lilv is a library for LV2 hosts intended to make using LV2 Plugins as simple | |||
as possible (without sacrificing capabilities). | |||
More information about LV2 plugins can be found at <http://lv2plug.in>. | |||
More information about Lilv can be found at <http://drobilla.net/software/lilv>. | |||
-- David Robillard <d@drobilla.net> |
@@ -0,0 +1,38 @@ | |||
%module lilv | |||
%{ | |||
#include "lilv/lilv.h" | |||
#include "lilv/lilvmm.hpp" | |||
%} | |||
%include "lilv/lilv.h" | |||
%include "lilv/lilvmm.hpp" | |||
namespace Lilv { | |||
%extend Plugins { | |||
%pythoncode %{ | |||
def __iter__(self): | |||
class Iterator(object): | |||
def __init__(self, plugins): | |||
self.plugins = plugins | |||
self.iter = plugins.begin() | |||
def next(self): | |||
self.iter = self.plugins.next(self.iter) | |||
if not self.plugins.is_end(self.iter): | |||
return self.plugins.get(self.iter) | |||
else: | |||
raise StopIteration | |||
return Iterator(self) | |||
%} | |||
}; | |||
%extend Node { | |||
%pythoncode %{ | |||
def __str__(self): | |||
return self.get_turtle_token() | |||
%} | |||
}; | |||
} /* namespace Lilv */ |
@@ -0,0 +1,96 @@ | |||
#!/usr/bin/env python | |||
# -*- coding: utf-8 -*- | |||
import math | |||
import lilv | |||
import sys | |||
import wave | |||
import numpy | |||
# Read command line arguments | |||
if len(sys.argv) != 4: | |||
print 'USAGE: lv2_apply.py PLUGIN_URI INPUT_WAV OUTPUT_WAV' | |||
sys.exit(1) | |||
# Initialise Lilv | |||
world = lilv.World() | |||
world.load_all() | |||
plugin_uri = world.new_uri(sys.argv[1]) | |||
wav_in_path = sys.argv[2] | |||
wav_out_path = sys.argv[3] | |||
# Find plugin | |||
plugin = world.get_all_plugins().get_by_uri(plugin_uri) | |||
if not plugin: | |||
print "Unknown plugin `%s'\n" % plugin_uri | |||
sys.exit(1) | |||
lv2_InputPort = world.new_uri(lilv.LILV_URI_INPUT_PORT) | |||
lv2_OutputPort = world.new_uri(lilv.LILV_URI_OUTPUT_PORT) | |||
lv2_AudioPort = world.new_uri(lilv.LILV_URI_AUDIO_PORT) | |||
n_audio_in = plugin.get_num_ports_of_class(lv2_InputPort, lv2_AudioPort) | |||
n_audio_out = plugin.get_num_ports_of_class(lv2_OutputPort, lv2_AudioPort) | |||
if n_audio_out == 0: | |||
print "Plugin has no audio outputs\n" | |||
sys.exit(1) | |||
# Open input file | |||
wav_in = wave.open(wav_in_path, 'r') | |||
if not wav_in: | |||
print "Failed to open input `%s'\n" % wav_in_path | |||
sys.exit(1) | |||
if wav_in.getnchannels() != n_audio_in: | |||
print "Input has %d channels, but plugin has %d audio inputs\n" % ( | |||
wav_in.getnchannels(), n_audio_in) | |||
sys.exit(1) | |||
# Open output file | |||
wav_out = wave.open(wav_out_path, 'w') | |||
if not wav_out: | |||
print "Failed to open output `%s'\n" % wav_out_path | |||
sys.exit(1) | |||
# Set output file to same format as input (except possibly nchannels) | |||
wav_out.setparams(wav_in.getparams()) | |||
wav_out.setnchannels(n_audio_out) | |||
rate = wav_in.getframerate() | |||
nframes = wav_in.getnframes() | |||
# Instantiate plugin | |||
instance = lilv.Instance(plugin, rate) | |||
def read_float(wf, nframes): | |||
wav = wf.readframes(nframes) | |||
if wf.getsampwidth() == 4: | |||
wav = wave.struct.unpack("<%ul" % (len(wav) / 4), wav) | |||
wav = [ i / float(math.pow(2, 32)) for i in wav ] | |||
elif wf.getsampwidth() == 2: | |||
wav = wave.struct.unpack("<%uh" % (len(wav) / 2), wav) | |||
wav = [ i / float(math.pow(2, 16)) for i in wav ] | |||
else: | |||
wav = wave.struct.unpack("%uB" % (len(wav)), wav) | |||
wav = [ s - 128 for s in wav ] | |||
wav = [ i / float(math.pow(2, 8)) for i in wav ] | |||
n_channels = wf.getnchannels() | |||
wavs = [] | |||
if n_channels > 1: | |||
for i in xrange(n_channels): | |||
wavs.append([ wav[j] for j in xrange(0, len(wav), n_channels) ]) | |||
else: | |||
wavs = [ wav ] | |||
return wavs | |||
in_buf = read_float(wav_in, nframes) | |||
# TODO: buffer marshaling | |||
#instance.connect_port(3, in_buf) | |||
print '%s => %s => %s @ %d Hz' % (wav_in_path, plugin.get_name(), wav_out_path, rate) | |||
instance.connect_port(3, in_buf) |
@@ -0,0 +1,9 @@ | |||
#!/usr/bin/env python | |||
import lilv | |||
world = lilv.World() | |||
world.load_all() | |||
for i in world.get_all_plugins(): | |||
print(i.get_uri()) |
@@ -0,0 +1,33 @@ | |||
.TH LV2INFO 1 "8 Jan 2012" | |||
.SH NAME | |||
.B lv2info \- print information about an installed LV2 plugin. | |||
.SH SYNOPSIS | |||
.B lv2info PLUGIN_URI | |||
.SH OPTIONS | |||
.TP | |||
\fB\-p FILE | |||
Write Turtle description of plugin to FILE | |||
.TP | |||
\fB\-m FILE | |||
Add record of plugin to manifest FILE | |||
.TP | |||
\fB\-\-help\fR | |||
Display help and exit | |||
.TP | |||
\fB\-\-version\fR | |||
Display version information and exit | |||
.SH SEE ALSO | |||
.BR lilv(3), | |||
.BR lv2ls(1) | |||
.SH AUTHOR | |||
lv2info was written by David Robillard <d@drobilla.net> | |||
.PP | |||
This manual page was written by Jaromír Mikes <mira.mikes@seznam.cz> | |||
and David Robillard <d@drobilla.net> |
@@ -0,0 +1,30 @@ | |||
.TH LV2LS 1 "17 Jan 2012" | |||
.SH NAME | |||
.B lv2ls \- List all installed LV2 plugins. | |||
.SH SYNOPSIS | |||
.B lv2ls [OPTION]... | |||
.SH OPTIONS | |||
.TP | |||
\fB\-n\fR, \fB\-\-names\fR | |||
Show names instead of URIs | |||
.TP | |||
\fB\-\-help\fR | |||
Display help and exit | |||
.TP | |||
\fB\-\-version\fR | |||
Display version information and exit | |||
.SH SEE ALSO | |||
.BR lilv(3), | |||
.BR lv2info(1) | |||
.SH AUTHOR | |||
lv2ls was written by David Robillard <d@drobilla.net> | |||
.PP | |||
This manual page was written by Jaromír Mikes <mira.mikes@seznam.cz> | |||
and David Robillard <d@drobilla.net> |
@@ -0,0 +1,563 @@ | |||
body { | |||
font-size: medium; | |||
font-family: sans-serif; | |||
} | |||
#top { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
padding: 0; | |||
border-bottom: 1px solid #DDD; | |||
margin-bottom: 1ex; | |||
font-size: xx-large; | |||
font-weight: bold; | |||
} | |||
div.header { | |||
display: none; | |||
} | |||
.tabs { | |||
display: none; | |||
} | |||
h1 h2 h3 h4 h5 h6 { | |||
font-weight: bold; | |||
} | |||
h1 { | |||
font-size: 164%; | |||
} | |||
h2 { | |||
font-size: 132%; | |||
} | |||
h3 { | |||
font-size: 124%; | |||
} | |||
h4 { | |||
font-size: 116%; | |||
} | |||
h5 { | |||
font-size: 108%; | |||
} | |||
h6 { | |||
font-size: 100%; | |||
} | |||
p { | |||
margin: 0 0 1ex 0; | |||
} | |||
br { | |||
display: none; | |||
} | |||
dt { | |||
font-weight: 700; | |||
} | |||
div.multicol { | |||
} | |||
p.startli,p.startdd,p.starttd { | |||
margin-top: 2px; | |||
} | |||
p.endli { | |||
margin-bottom: 0; | |||
} | |||
p.enddd { | |||
margin-bottom: 4px; | |||
} | |||
p.endtd { | |||
margin-bottom: 2px; | |||
} | |||
caption { | |||
font-weight: 700; | |||
} | |||
span.legend { | |||
font-size: 70%; | |||
text-align: center; | |||
} | |||
h3.version { | |||
font-size: 90%; | |||
text-align: center; | |||
} | |||
div.qindex,div.navtab { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
padding: 2px; | |||
} | |||
div.qindex,div.navpath { | |||
width: 100%; | |||
line-height: 140%; | |||
} | |||
div.navtab { | |||
margin-right: 15px; | |||
} | |||
/* @group Link Styling */ | |||
a { | |||
color: #3D8C57; | |||
text-decoration: none; | |||
} | |||
.contents a:visited { | |||
color: #50755E; | |||
} | |||
a:hover { | |||
text-decoration: underline; | |||
} | |||
a.qindexHL { | |||
background-color: #9CAFD4; | |||
color: #FFF; | |||
border: 1px double #869DCA; | |||
} | |||
a.code { | |||
color: #4665A2; | |||
} | |||
a.codeRef { | |||
color: #4665A2; | |||
} | |||
/* @end */ | |||
dl.el { | |||
margin-left: -1cm; | |||
} | |||
.fragment { | |||
font-family: monospace, fixed; | |||
font-size: 105%; | |||
} | |||
pre.fragment { | |||
border: 1px solid #C4C4C4; | |||
background-color: #F9F9F9; | |||
padding: 4px 6px; | |||
margin: 4px 8px 4px 2px; | |||
overflow: auto; | |||
font-size: 9pt; | |||
line-height: 125%; | |||
} | |||
div.ah { | |||
background-color: #000; | |||
font-weight: 700; | |||
color: #FFF; | |||
margin-bottom: 3px; | |||
margin-top: 3px; | |||
padding: .2em; | |||
border: thin solid #333; | |||
} | |||
div.groupHeader { | |||
margin-left: 16px; | |||
margin-top: 12px; | |||
margin-bottom: 6px; | |||
font-weight: 700; | |||
} | |||
div.groupText { | |||
margin-left: 16px; | |||
font-style: italic; | |||
} | |||
body { | |||
background: #FFF; | |||
color: #000; | |||
margin: 0; | |||
} | |||
div.contents { | |||
margin-top: 10px; | |||
margin-left: 10px; | |||
margin-right: 10px; | |||
} | |||
td.indexkey { | |||
background-color: #EBEFF6; | |||
font-weight: 700; | |||
border: 1px solid #C4CFE5; | |||
margin: 2px 0; | |||
padding: 2px 10px; | |||
} | |||
td.indexvalue { | |||
background-color: #EBEFF6; | |||
border: 1px solid #C4CFE5; | |||
padding: 2px 10px; | |||
margin: 2px 0; | |||
} | |||
tr.memlist { | |||
background-color: #EEF1F7; | |||
} | |||
p.formulaDsp { | |||
text-align: center; | |||
} | |||
img.formulaDsp { | |||
} | |||
img.formulaInl { | |||
vertical-align: middle; | |||
} | |||
div.center { | |||
text-align: center; | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
padding: 0; | |||
} | |||
div.center img { | |||
border: 0; | |||
} | |||
address.footer { | |||
text-align: right; | |||
padding: 0 0.25em 0.25em 0; | |||
} | |||
img.footer { | |||
border: 0; | |||
vertical-align: middle; | |||
} | |||
/* @group Code Colorization */ | |||
span.keyword { | |||
color: green; | |||
} | |||
span.keywordtype { | |||
color: #604020; | |||
} | |||
span.keywordflow { | |||
color: #e08000; | |||
} | |||
span.comment { | |||
color: maroon; | |||
} | |||
span.preprocessor { | |||
color: #806020; | |||
} | |||
span.stringliteral { | |||
color: #002080; | |||
} | |||
span.charliteral { | |||
color: teal; | |||
} | |||
span.vhdldigit { | |||
color: #F0F; | |||
} | |||
span.vhdlkeyword { | |||
color: #700070; | |||
} | |||
span.vhdllogic { | |||
color: red; | |||
} | |||
/* @end */ | |||
td.tiny { | |||
font-size: 75%; | |||
} | |||
.dirtab { | |||
padding: 4px; | |||
border-collapse: collapse; | |||
border: 1px solid #A3B4D7; | |||
} | |||
th.dirtab { | |||
background: #EBEFF6; | |||
font-weight: 700; | |||
} | |||
hr { | |||
height: 0; | |||
border: none; | |||
border-top: 1px solid #DDD; | |||
margin: 2em 0 1em; | |||
} | |||
hr.footer { | |||
height: 1px; | |||
} | |||
/* @group Member Descriptions */ | |||
table.memberdecls { | |||
border-spacing: 0; | |||
font-size: small; | |||
} | |||
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
background-color: #FBFBFB; | |||
margin: 0; | |||
padding: 0.25ex; | |||
} | |||
.mdescLeft,.mdescRight { | |||
color: #555; | |||
} | |||
.memItemLeft,.memItemRight,.memTemplParams { | |||
border-top: 1px solid #DDD; | |||
} | |||
.memItemLeft,.memTemplItemLeft { | |||
white-space: nowrap; | |||
padding-left: 2em; | |||
} | |||
.memTemplParams { | |||
color: #464646; | |||
white-space: nowrap; | |||
} | |||
/* @end */ | |||
/* @group Member Details */ | |||
/* Styles for detailed member documentation */ | |||
.memtemplate { | |||
font-size: 80%; | |||
color: #4665A2; | |||
font-weight: bold; | |||
} | |||
.memnav { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
margin-right: 15px; | |||
padding: 2px; | |||
} | |||
.memitem { | |||
padding: 0; | |||
margin: 1ex 0 2ex 0; | |||
border: 1px solid #CCC; | |||
} | |||
.memname { | |||
white-space: nowrap; | |||
font-weight: bold; | |||
} | |||
.memproto { | |||
border-bottom: 1px solid #DDD; | |||
padding: 0.5ex; | |||
font-weight: bold; | |||
background-color: #F3F3F3; | |||
} | |||
.memdoc { | |||
padding: 1ex; | |||
background-color: #FBFBFB; | |||
border-top-width: 0; | |||
} | |||
.paramkey { | |||
text-align: right; | |||
} | |||
.paramtype { | |||
white-space: nowrap; | |||
} | |||
.paramname { | |||
color: #602020; | |||
white-space: nowrap; | |||
} | |||
.paramname em { | |||
font-style: normal; | |||
} | |||
/* @end */ | |||
/* @group Directory (tree) */ | |||
/* for the tree view */ | |||
.ftvtree { | |||
font-family: sans-serif; | |||
margin: 0; | |||
} | |||
/* these are for tree view when used as main index */ | |||
.directory { | |||
font-size: 9pt; | |||
font-weight: bold; | |||
margin: 5px; | |||
} | |||
.directory h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory > h3 { | |||
margin-top: 0; | |||
} | |||
.directory p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory img { | |||
vertical-align: -30%; | |||
} | |||
/* these are for tree view when not used as main index */ | |||
.directory-alt { | |||
font-size: 100%; | |||
font-weight: bold; | |||
} | |||
.directory-alt h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory-alt > h3 { | |||
margin-top: 0; | |||
} | |||
.directory-alt p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory-alt div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory-alt img { | |||
vertical-align: -30%; | |||
} | |||
/* @end */ | |||
div.dynheader { | |||
margin-top: 8px; | |||
} | |||
address { | |||
font-style: normal; | |||
color: #2A3D61; | |||
} | |||
table.doxtable { | |||
border-collapse: collapse; | |||
margin: 0.5ex; | |||
} | |||
table.doxtable td,table.doxtable th { | |||
border: 1px solid #DDD; | |||
padding: 3px 7px 2px; | |||
} | |||
table.doxtable th { | |||
background-color: #F3F3F3; | |||
color: #000; | |||
padding-bottom: 4px; | |||
padding-top: 5px; | |||
text-align: left; | |||
font-weight: bold; | |||
} | |||
.tabsearch { | |||
top: 0; | |||
left: 10px; | |||
height: 36px; | |||
z-index: 101; | |||
overflow: hidden; | |||
font-size: 13px; | |||
} | |||
.navpath ul { | |||
font-size: 11px; | |||
height: 30px; | |||
line-height: 30px; | |||
color: #8AA0CC; | |||
border: 1px solid #C2CDE4; | |||
overflow: hidden; | |||
margin: 0; | |||
padding: 0; | |||
} | |||
.navpath li { | |||
list-style-type: none; | |||
float: left; | |||
padding-left: 10px; | |||
padding-right: 15px; | |||
color: #364D7C; | |||
} | |||
.navpath a { | |||
height: 32px; | |||
display: block; | |||
text-decoration: none; | |||
outline: none; | |||
} | |||
.navpath a:hover { | |||
color: #6884BD; | |||
} | |||
div.summary { | |||
float: right; | |||
font-size: 8pt; | |||
padding-right: 5px; | |||
width: 50%; | |||
text-align: right; | |||
} | |||
div.summary a { | |||
white-space: nowrap; | |||
} | |||
div.header { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
border-bottom: 1px solid #DDD; | |||
} | |||
div.headertitle { | |||
padding: 5px 5px 5px 10px; | |||
font-size: 180%; | |||
font-weight: bold; | |||
} |
@@ -0,0 +1,11 @@ | |||
prefix=@PREFIX@ | |||
exec_prefix=@EXEC_PREFIX@ | |||
libdir=@LIBDIR@ | |||
includedir=@INCLUDEDIR@ | |||
Name: Lilv | |||
Version: @LILV_VERSION@ | |||
Description: Simple C library for hosting LV2 plugins | |||
Requires: lv2core @LILV_PKG_DEPS@ | |||
Libs: -L${libdir} -l@LIB_LILV@ -ldl | |||
Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@ |
@@ -0,0 +1,29 @@ | |||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
@prefix : <http://usefulinc.com/ns/doap#> . | |||
@prefix foaf: <http://xmlns.com/foaf/0.1/> . | |||
<http://drobilla.net/software/lilv> | |||
a :Project ; | |||
:bug-database <http://dev.drobilla.net/query?status=new&status=assigned&status=reopened&component=LILV&order=priority> ; | |||
:developer [ | |||
a foaf:Person ; | |||
rdfs:seeAlso <http://drobilla.net/drobilla.rdf> ; | |||
foaf:homepage <http://drobilla.net> ; | |||
foaf:mbox_sha1sum "253b3c58086250260bac1232d744d150274ad308" ; | |||
foaf:name "David Robillard" | |||
] ; | |||
:download-page <http://download.drobilla.net> ; | |||
:homepage <http://drobilla.net/software/lilv> ; | |||
:license <http://usefulinc.com/doap/licenses/gpl> ; | |||
:name "LILV" ; | |||
:programming-language "C", "Turtle" ; | |||
:repository [ | |||
:browse <http://dev.drobilla.net/browser/trunk/lilv> ; | |||
:location <http://svn.drobilla.net/lad/trunk/lilv> ; | |||
a :SVNRepository | |||
] ; | |||
:shortdesc "Library for simple use of LV2 plugins" ; | |||
:shortname "LILV" . | |||
@@ -0,0 +1,332 @@ | |||
/* | |||
Copyright 2007-2011 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 LILV_LILVMM_HPP | |||
#define LILV_LILVMM_HPP | |||
#include "lilv/lilv.h" | |||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) | |||
# define LILV_DEPRECATED __attribute__((__deprecated__)) | |||
#else | |||
# define LILV_DEPRECATED | |||
#endif | |||
namespace Lilv { | |||
static inline const char* | |||
uri_to_path(const char* uri) { | |||
return lilv_uri_to_path(uri); | |||
} | |||
#define LILV_WRAP0(RT, prefix, name) \ | |||
inline RT name() { return lilv_ ## prefix ## _ ## name (me); } | |||
#define LILV_WRAP0_VOID(prefix, name) \ | |||
inline void name() { lilv_ ## prefix ## _ ## name(me); } | |||
#define LILV_WRAP1(RT, prefix, name, T1, a1) \ | |||
inline RT name(T1 a1) { return lilv_ ## prefix ## _ ## name (me, a1); } | |||
#define LILV_WRAP1_VOID(prefix, name, T1, a1) \ | |||
inline void name(T1 a1) { lilv_ ## prefix ## _ ## name(me, a1); } | |||
#define LILV_WRAP2(RT, prefix, name, T1, a1, T2, a2) \ | |||
inline RT name(T1 a1, T2 a2) { \ | |||
return lilv_ ## prefix ## _ ## name(me, a1, a2); \ | |||
} | |||
#define LILV_WRAP2_VOID(prefix, name, T1, a1, T2, a2) \ | |||
inline void name(T1 a1, T2 a2) { lilv_ ## prefix ## _ ## name(me, a1, a2); } | |||
#ifndef SWIG | |||
#define LILV_WRAP_CONVERSION(CT) \ | |||
inline operator CT*() const { return me; } | |||
#else | |||
#define LILV_WRAP_CONVERSION(CT) | |||
#endif | |||
struct Node { | |||
inline Node(LilvNode* node) : me(node) {} | |||
inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {} | |||
inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {} | |||
inline ~Node() { lilv_node_free(me); } | |||
inline bool equals(const Node& other) const { | |||
return lilv_node_equals(me, other.me); | |||
} | |||
inline bool operator==(const Node& other) const { return equals(other); } | |||
LILV_WRAP_CONVERSION(LilvNode); | |||
LILV_WRAP0(char*, node, get_turtle_token); | |||
LILV_WRAP0(bool, node, is_uri); | |||
LILV_WRAP0(const char*, node, as_uri); | |||
LILV_WRAP0(bool, node, is_blank); | |||
LILV_WRAP0(const char*, node, as_blank); | |||
LILV_WRAP0(bool, node, is_literal); | |||
LILV_WRAP0(bool, node, is_string); | |||
LILV_WRAP0(const char*, node, as_string); | |||
LILV_WRAP0(bool, node, is_float); | |||
LILV_WRAP0(float, node, as_float); | |||
LILV_WRAP0(bool, node, is_int); | |||
LILV_WRAP0(int, node, as_int); | |||
LILV_WRAP0(bool, node, is_bool); | |||
LILV_WRAP0(bool, node, as_bool); | |||
LilvNode* me; | |||
}; | |||
struct ScalePoint { | |||
inline ScalePoint(const LilvScalePoint* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvScalePoint); | |||
LILV_WRAP0(const LilvNode*, scale_point, get_label); | |||
LILV_WRAP0(const LilvNode*, scale_point, get_value); | |||
const LilvScalePoint* me; | |||
}; | |||
struct PluginClass { | |||
inline PluginClass(const LilvPluginClass* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvPluginClass); | |||
LILV_WRAP0(Node, plugin_class, get_parent_uri); | |||
LILV_WRAP0(Node, plugin_class, get_uri); | |||
LILV_WRAP0(Node, plugin_class, get_label); | |||
LILV_WRAP0(LilvPluginClasses*, plugin_class, get_children); | |||
const LilvPluginClass* me; | |||
}; | |||
#define LILV_WRAP_COLL(CT, ET, prefix) \ | |||
inline CT(const Lilv ## CT* c_obj) : me(c_obj) {} \ | |||
LILV_WRAP_CONVERSION(const Lilv ## CT); \ | |||
LILV_WRAP0(unsigned, prefix, size); \ | |||
LILV_WRAP1(const ET, prefix, get, LilvIter*, i); \ | |||
LILV_WRAP0(LilvIter*, prefix, begin); \ | |||
LILV_WRAP1(LilvIter*, prefix, next, LilvIter*, i); \ | |||
LILV_WRAP1(bool, prefix, is_end, LilvIter*, i); \ | |||
const Lilv ## CT* me; \ | |||
struct PluginClasses { | |||
LILV_WRAP_COLL(PluginClasses, PluginClass, plugin_classes); | |||
LILV_WRAP1(const PluginClass, plugin_classes, | |||
get_by_uri, const LilvNode*, uri); | |||
}; | |||
struct ScalePoints { | |||
LILV_WRAP_COLL(ScalePoints, ScalePoint, scale_points); | |||
}; | |||
struct Nodes { | |||
LILV_WRAP_COLL(Nodes, Node, nodes); | |||
LILV_WRAP1(bool, nodes, contains, const Node, node); | |||
LILV_WRAP0(Node, nodes, get_first); | |||
}; | |||
struct Port { | |||
inline Port(const LilvPlugin* p, const LilvPort* c_obj) | |||
: parent(p), me(c_obj) | |||
{} | |||
LILV_WRAP_CONVERSION(const LilvPort); | |||
#define LILV_PORT_WRAP0(RT, name) \ | |||
inline RT name () { return lilv_port_ ## name (parent, me); } | |||
#define LILV_PORT_WRAP1(RT, name, T1, a1) \ | |||
inline RT name (T1 a1) { return lilv_port_ ## name (parent, me, a1); } | |||
LILV_PORT_WRAP1(LilvNodes*, get_value, LilvNode*, predicate); | |||
LILV_PORT_WRAP0(LilvNodes*, get_properties) | |||
LILV_PORT_WRAP1(bool, has_property, LilvNode*, property_uri); | |||
LILV_PORT_WRAP1(bool, supports_event, LilvNode*, event_uri); | |||
LILV_PORT_WRAP0(const LilvNode*, get_symbol); | |||
LILV_PORT_WRAP0(LilvNode*, get_name); | |||
LILV_PORT_WRAP0(const LilvNodes*, get_classes); | |||
LILV_PORT_WRAP1(bool, is_a, LilvNode*, port_class); | |||
LILV_PORT_WRAP0(LilvScalePoints*, get_scale_points); | |||
// TODO: get_range (output parameters) | |||
const LilvPlugin* parent; | |||
const LilvPort* me; | |||
}; | |||
struct UI { | |||
inline UI(const LilvUI* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvUI); | |||
LILV_WRAP0(Node, ui, get_uri); | |||
LILV_WRAP1(bool, ui, is_a, LilvNode*, ui_class); | |||
LILV_WRAP0(Node, ui, get_bundle_uri); | |||
LILV_WRAP0(Node, ui, get_binary_uri); | |||
LILV_WRAP0(Nodes, ui, get_supported_features); | |||
LILV_WRAP0(Nodes, ui, get_required_features); | |||
LILV_WRAP0(Nodes, ui, get_optional_features); | |||
LILV_WRAP0(Nodes, ui, get_extension_data); | |||
const LilvUI* me; | |||
}; | |||
struct UIs { | |||
LILV_WRAP_COLL(UIs, UI, uis); | |||
}; | |||
struct Plugin { | |||
inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvPlugin); | |||
LILV_WRAP0(bool, plugin, verify); | |||
LILV_WRAP0(Node, plugin, get_uri); | |||
LILV_WRAP0(Node, plugin, get_bundle_uri); | |||
LILV_WRAP0(Nodes, plugin, get_data_uris); | |||
LILV_WRAP0(Node, plugin, get_library_uri); | |||
LILV_WRAP0(Node, plugin, get_name); | |||
LILV_WRAP0(PluginClass, plugin, get_class); | |||
LILV_WRAP1(Nodes, plugin, get_value, Node, pred); | |||
LILV_WRAP1(bool, plugin, has_feature, Node, feature_uri); | |||
LILV_WRAP0(Nodes, plugin, get_supported_features); | |||
LILV_WRAP0(Nodes, plugin, get_required_features); | |||
LILV_WRAP0(Nodes, plugin, get_optional_features); | |||
LILV_WRAP0(unsigned, plugin, get_num_ports); | |||
LILV_WRAP0(bool, plugin, has_latency); | |||
LILV_WRAP0(unsigned, plugin, get_latency_port_index); | |||
LILV_WRAP0(Node, plugin, get_author_name); | |||
LILV_WRAP0(Node, plugin, get_author_email); | |||
LILV_WRAP0(Node, plugin, get_author_homepage); | |||
LILV_WRAP0(bool, plugin, is_replaced); | |||
LILV_WRAP0(Nodes, plugin, get_extension_data); | |||
LILV_WRAP0(UIs, plugin, get_uis); | |||
inline Port get_port_by_index(unsigned index) { | |||
return Port(me, lilv_plugin_get_port_by_index(me, index)); | |||
} | |||
inline Port get_port_by_symbol(LilvNode* symbol) { | |||
return Port(me, lilv_plugin_get_port_by_symbol(me, symbol)); | |||
} | |||
inline void get_port_ranges_float(float* min_values, | |||
float* max_values, | |||
float* def_values) { | |||
return lilv_plugin_get_port_ranges_float( | |||
me, min_values, max_values, def_values); | |||
} | |||
inline unsigned get_num_ports_of_class(LilvNode* class_1, | |||
LilvNode* class_2) { | |||
// TODO: varargs | |||
return lilv_plugin_get_num_ports_of_class(me, class_1, class_2, NULL); | |||
} | |||
const LilvPlugin* me; | |||
}; | |||
struct Plugins { | |||
LILV_WRAP_COLL(Plugins, Plugin, plugins); | |||
LILV_WRAP1(const Plugin, plugins, get_by_uri, const LilvNode*, uri); | |||
}; | |||
struct Instance { | |||
inline Instance(LilvInstance* instance) : me(instance) {} | |||
LILV_DEPRECATED | |||
inline Instance(Plugin plugin, double sample_rate) { | |||
me = lilv_plugin_instantiate(plugin, sample_rate, NULL); | |||
} | |||
LILV_DEPRECATED inline Instance(Plugin plugin, | |||
double sample_rate, | |||
LV2_Feature* const* features) { | |||
me = lilv_plugin_instantiate(plugin, sample_rate, features); | |||
} | |||
static inline Instance* create(Plugin plugin, | |||
double sample_rate, | |||
LV2_Feature* const* features) { | |||
LilvInstance* me = lilv_plugin_instantiate( | |||
plugin, sample_rate, features); | |||
return me ? new Instance(me) : NULL; | |||
} | |||
LILV_WRAP_CONVERSION(LilvInstance); | |||
LILV_WRAP2_VOID(instance, connect_port, | |||
unsigned, port_index, | |||
void*, data_location); | |||
LILV_WRAP0_VOID(instance, activate); | |||
LILV_WRAP1_VOID(instance, run, unsigned, sample_count); | |||
LILV_WRAP0_VOID(instance, deactivate); | |||
inline const void* get_extension_data(const char* uri) { | |||
return lilv_instance_get_extension_data(me, uri); | |||
} | |||
inline const LV2_Descriptor* get_descriptor() { | |||
return lilv_instance_get_descriptor(me); | |||
} | |||
inline LV2_Handle get_handle() { | |||
return lilv_instance_get_handle(me); | |||
} | |||
LilvInstance* me; | |||
}; | |||
struct World { | |||
inline World() : me(lilv_world_new()) {} | |||
inline ~World() { lilv_world_free(me); } | |||
inline LilvNode* new_uri(const char* uri) { | |||
return lilv_new_uri(me, uri); | |||
} | |||
inline LilvNode* new_string(const char* str) { | |||
return lilv_new_string(me, str); | |||
} | |||
inline LilvNode* new_int(int val) { | |||
return lilv_new_int(me, val); | |||
} | |||
inline LilvNode* new_float(float val) { | |||
return lilv_new_float(me, val); | |||
} | |||
inline LilvNode* new_bool(bool val) { | |||
return lilv_new_bool(me, val); | |||
} | |||
inline Nodes find_nodes(const LilvNode* subject, | |||
const LilvNode* predicate, | |||
const LilvNode* object) { | |||
return lilv_world_find_nodes(me, subject, predicate, object); | |||
} | |||
LILV_WRAP2_VOID(world, set_option, const char*, uri, LilvNode*, value); | |||
LILV_WRAP0_VOID(world, load_all); | |||
LILV_WRAP1_VOID(world, load_bundle, LilvNode*, bundle_uri); | |||
LILV_WRAP0(const LilvPluginClass*, world, get_plugin_class); | |||
LILV_WRAP0(const LilvPluginClasses*, world, get_plugin_classes); | |||
LILV_WRAP0(const Plugins, world, get_all_plugins); | |||
LilvWorld* me; | |||
}; | |||
} /* namespace Lilv */ | |||
#endif /* LILV_LILVMM_HPP */ |
@@ -0,0 +1,214 @@ | |||
/* | |||
Copyright 2008-2011 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 "lilv_internal.h" | |||
int | |||
lilv_ptr_cmp(const void* a, const void* b, void* user_data) | |||
{ | |||
return (intptr_t)a - (intptr_t)b; | |||
} | |||
int | |||
lilv_resource_node_cmp(const void* a, const void* b, void* user_data) | |||
{ | |||
const SordNode* an = ((const LilvNode*)a)->node; | |||
const SordNode* bn = ((const LilvNode*)b)->node; | |||
return (intptr_t)an - (intptr_t)bn; | |||
} | |||
/* Generic collection functions */ | |||
static inline LilvCollection* | |||
lilv_collection_new(ZixComparator cmp, ZixDestroyFunc destructor) | |||
{ | |||
return zix_tree_new(false, cmp, NULL, destructor); | |||
} | |||
void | |||
lilv_collection_free(LilvCollection* coll) | |||
{ | |||
if (coll) | |||
zix_tree_free((ZixTree*)coll); | |||
} | |||
unsigned | |||
lilv_collection_size(const LilvCollection* coll) | |||
{ | |||
return (coll ? zix_tree_size((const ZixTree*)coll) : 0); | |||
} | |||
LilvIter* | |||
lilv_collection_begin(const LilvCollection* collection) | |||
{ | |||
if (collection) { | |||
return (LilvIter*)zix_tree_begin((ZixTree*)collection); | |||
} | |||
return NULL; | |||
} | |||
void* | |||
lilv_collection_get(const LilvCollection* collection, | |||
const LilvIter* i) | |||
{ | |||
return zix_tree_get((ZixTreeIter*)i); | |||
} | |||
/* Constructors */ | |||
LilvScalePoints* | |||
lilv_scale_points_new(void) | |||
{ | |||
return lilv_collection_new(lilv_ptr_cmp, | |||
(ZixDestroyFunc)lilv_scale_point_free); | |||
} | |||
LilvNodes* | |||
lilv_nodes_new(void) | |||
{ | |||
return lilv_collection_new(lilv_ptr_cmp, | |||
(ZixDestroyFunc)lilv_node_free); | |||
} | |||
LilvUIs* | |||
lilv_uis_new(void) | |||
{ | |||
return lilv_collection_new(lilv_header_compare_by_uri, | |||
(ZixDestroyFunc)lilv_ui_free); | |||
} | |||
LilvPluginClasses* | |||
lilv_plugin_classes_new(void) | |||
{ | |||
return lilv_collection_new(lilv_header_compare_by_uri, | |||
(ZixDestroyFunc)lilv_plugin_class_free); | |||
} | |||
/* URI based accessors (for collections of things with URIs) */ | |||
LILV_API | |||
const LilvPluginClass* | |||
lilv_plugin_classes_get_by_uri(const LilvPluginClasses* coll, | |||
const LilvNode* uri) | |||
{ | |||
return (LilvPluginClass*)lilv_collection_get_by_uri( | |||
(const ZixTree*)coll, uri); | |||
} | |||
LILV_API | |||
const LilvUI* | |||
lilv_uis_get_by_uri(const LilvUIs* coll, const LilvNode* uri) | |||
{ | |||
return (LilvUI*)lilv_collection_get_by_uri((const ZixTree*)coll, uri); | |||
} | |||
/* Plugins */ | |||
LilvPlugins* | |||
lilv_plugins_new(void) | |||
{ | |||
return lilv_collection_new(lilv_header_compare_by_uri, NULL); | |||
} | |||
LILV_API | |||
const LilvPlugin* | |||
lilv_plugins_get_by_uri(const LilvPlugins* list, const LilvNode* uri) | |||
{ | |||
return (LilvPlugin*)lilv_collection_get_by_uri((const ZixTree*)list, uri); | |||
} | |||
/* Nodes */ | |||
LILV_API | |||
bool | |||
lilv_nodes_contains(const LilvNodes* list, const LilvNode* value) | |||
{ | |||
LILV_FOREACH(nodes, i, list) | |||
if (lilv_node_equals(lilv_nodes_get(list, i), value)) | |||
return true; | |||
return false; | |||
} | |||
/* Iterator */ | |||
#define LILV_COLLECTION_IMPL(prefix, CT, ET) \ | |||
LILV_API \ | |||
unsigned \ | |||
prefix##_size(const CT* collection) { \ | |||
return lilv_collection_size(collection); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
LilvIter* \ | |||
prefix##_begin(const CT* collection) { \ | |||
return lilv_collection_begin(collection); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
const ET* \ | |||
prefix##_get(const CT* collection, LilvIter* i) { \ | |||
return (ET*)lilv_collection_get(collection, i); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
LilvIter* \ | |||
prefix##_next(const CT* collection, LilvIter* i) { \ | |||
return zix_tree_iter_next((ZixTreeIter*)i); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
bool \ | |||
prefix##_is_end(const CT* collection, LilvIter* i) { \ | |||
return zix_tree_iter_is_end((ZixTreeIter*)i); \ | |||
} | |||
LILV_COLLECTION_IMPL(lilv_plugin_classes, LilvPluginClasses, LilvPluginClass) | |||
LILV_COLLECTION_IMPL(lilv_scale_points, LilvScalePoints, LilvScalePoint) | |||
LILV_COLLECTION_IMPL(lilv_uis, LilvUIs, LilvUI) | |||
LILV_COLLECTION_IMPL(lilv_nodes, LilvNodes, LilvNode) | |||
LILV_COLLECTION_IMPL(lilv_plugins, LilvPlugins, LilvPlugin) | |||
LILV_API | |||
void | |||
lilv_plugin_classes_free(LilvPluginClasses* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
void | |||
lilv_scale_points_free(LilvScalePoints* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
void | |||
lilv_uis_free(LilvUIs* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
void | |||
lilv_nodes_free(LilvNodes* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_nodes_get_first(const LilvNodes* collection) { | |||
return (LilvNode*)lilv_collection_get(collection, | |||
lilv_collection_begin(collection)); | |||
} |
@@ -0,0 +1,126 @@ | |||
/* | |||
Copyright 2007-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 <assert.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
LILV_API | |||
LilvInstance* | |||
lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
double sample_rate, | |||
const LV2_Feature*const* features) | |||
{ | |||
lilv_plugin_load_if_necessary(plugin); | |||
LilvInstance* result = NULL; | |||
const LV2_Feature** local_features = NULL; | |||
if (features == NULL) { | |||
local_features = (const LV2_Feature**)malloc(sizeof(LV2_Feature)); | |||
local_features[0] = NULL; | |||
} | |||
const LilvNode* const lib_uri = lilv_plugin_get_library_uri(plugin); | |||
const LilvNode* const bundle_uri = lilv_plugin_get_bundle_uri(plugin); | |||
const char* bundle_path = lilv_uri_to_path( | |||
lilv_node_as_uri(lilv_plugin_get_bundle_uri(plugin))); | |||
LilvLib* lib = lilv_lib_open(plugin->world, lib_uri, bundle_path, features); | |||
if (!lib) { | |||
return NULL; | |||
} | |||
// Parse bundle URI to use as base URI | |||
const char* bundle_uri_str = lilv_node_as_uri(bundle_uri); | |||
SerdURI base_uri; | |||
if (serd_uri_parse((const uint8_t*)bundle_uri_str, &base_uri)) { | |||
lilv_lib_close(lib); | |||
return NULL; | |||
} | |||
// Search for plugin by URI | |||
for (uint32_t i = 0; true; ++i) { | |||
const LV2_Descriptor* ld = lilv_lib_get_plugin(lib, i); | |||
if (!ld) { | |||
LILV_ERRORF("No plugin <%s> in <%s>\n", | |||
lilv_node_as_uri(lilv_plugin_get_uri(plugin)), | |||
lilv_node_as_uri(lib_uri)); | |||
lilv_lib_close(lib); | |||
break; // return NULL | |||
} | |||
// Resolve library plugin URI against base URI | |||
SerdURI abs_uri; | |||
SerdNode abs_uri_node = serd_node_new_uri_from_string( | |||
(const uint8_t*)ld->URI, &base_uri, &abs_uri); | |||
if (!abs_uri_node.buf) { | |||
LILV_ERRORF("Failed to parse plugin URI `%s'\n", ld->URI); | |||
lilv_lib_close(lib); | |||
break; | |||
} | |||
if (!strcmp((const char*)abs_uri_node.buf, | |||
lilv_node_as_uri(lilv_plugin_get_uri(plugin)))) { | |||
// Create LilvInstance to return | |||
result = (LilvInstance*)malloc(sizeof(LilvInstance)); | |||
result->lv2_descriptor = ld; | |||
result->lv2_handle = ld->instantiate( | |||
ld, sample_rate, bundle_path, | |||
(features) ? features : local_features); | |||
result->pimpl = lib; | |||
serd_node_free(&abs_uri_node); | |||
break; | |||
} else { | |||
serd_node_free(&abs_uri_node); | |||
} | |||
} | |||
if (result) { | |||
// Failed to instantiate | |||
if (result->lv2_handle == NULL) { | |||
free(result); | |||
return NULL; | |||
} | |||
// "Connect" all ports to NULL (catches bugs) | |||
for (uint32_t i = 0; i < lilv_plugin_get_num_ports(plugin); ++i) | |||
result->lv2_descriptor->connect_port(result->lv2_handle, i, NULL); | |||
} | |||
free(local_features); | |||
return result; | |||
} | |||
LILV_API | |||
void | |||
lilv_instance_free(LilvInstance* instance) | |||
{ | |||
if (!instance) | |||
return; | |||
instance->lv2_descriptor->cleanup(instance->lv2_handle); | |||
instance->lv2_descriptor = NULL; | |||
lilv_lib_close((LilvLib*)instance->pimpl); | |||
instance->pimpl = NULL; | |||
free(instance); | |||
} | |||
@@ -0,0 +1,111 @@ | |||
/* | |||
Copyright 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 "lilv_internal.h" | |||
LilvLib* | |||
lilv_lib_open(LilvWorld* world, | |||
const LilvNode* uri, | |||
const char* bundle_path, | |||
const LV2_Feature*const* features) | |||
{ | |||
ZixTreeIter* i = NULL; | |||
const struct LilvHeader key = { world, (LilvNode*)uri }; | |||
if (!zix_tree_find(world->libs, &key, &i)) { | |||
LilvLib* llib = (LilvLib*)zix_tree_get(i); | |||
++llib->refs; | |||
return llib; | |||
} | |||
const char* const lib_uri = lilv_node_as_uri(uri); | |||
const char* const lib_path = lilv_uri_to_path(lib_uri); | |||
if (!lib_path) { | |||
return NULL; | |||
} | |||
dlerror(); | |||
void* lib = dlopen(lib_path, RTLD_NOW); | |||
if (!lib) { | |||
LILV_ERRORF("Failed to open library %s (%s)\n", lib_path, dlerror()); | |||
return NULL; | |||
} | |||
LV2_Descriptor_Function df = (LV2_Descriptor_Function) | |||
lilv_dlfunc(lib, "lv2_descriptor"); | |||
#ifdef LILV_NEW_LV2 | |||
LV2_Lib_Descriptor_Function ldf = (LV2_Lib_Descriptor_Function) | |||
lilv_dlfunc(lib, "lv2_lib_descriptor"); | |||
const LV2_Lib_Descriptor* desc = NULL; | |||
if (ldf) { | |||
desc = ldf(bundle_path, features); | |||
if (!desc) { | |||
LILV_ERRORF("Call to `lv2_lib_descriptor' in %s failed\n", lib_path); | |||
return NULL; | |||
} | |||
} else | |||
#endif | |||
if (!df) { | |||
LILV_ERRORF("No `lv2_descriptor' or `lv2_lib_descriptor' in %s\n", | |||
lib_path); | |||
dlclose(lib); | |||
return NULL; | |||
} | |||
LilvLib* llib = (LilvLib*)malloc(sizeof(LilvLib)); | |||
llib->world = world; | |||
llib->uri = lilv_node_duplicate(uri); | |||
llib->lib = lib; | |||
llib->lv2_descriptor = df; | |||
#ifdef LILV_NEW_LV2 | |||
llib->desc = desc; | |||
#endif | |||
llib->refs = 1; | |||
zix_tree_insert(world->libs, llib, NULL); | |||
return llib; | |||
} | |||
const LV2_Descriptor* | |||
lilv_lib_get_plugin(LilvLib* lib, uint32_t index) | |||
{ | |||
if (lib->lv2_descriptor) { | |||
return lib->lv2_descriptor(index); | |||
} | |||
#ifdef LILV_NEW_LV2 | |||
if (lib->desc) { | |||
return lib->desc->get_plugin(lib->desc->handle, index); | |||
} | |||
#endif | |||
return NULL; | |||
} | |||
void | |||
lilv_lib_close(LilvLib* lib) | |||
{ | |||
if (--lib->refs == 0) { | |||
dlclose(lib->lib); | |||
ZixTreeIter* i = NULL; | |||
if (lib->world->libs && !zix_tree_find(lib->world->libs, lib, &i)) { | |||
zix_tree_remove(lib->world->libs, i); | |||
} | |||
lilv_node_free(lib->uri); | |||
free(lib); | |||
} | |||
} |
@@ -0,0 +1,382 @@ | |||
/* | |||
Copyright 2007-2011 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 LILV_INTERNAL_H | |||
#define LILV_INTERNAL_H | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <float.h> | |||
#ifdef _WIN32 | |||
# include <windows.h> | |||
# define dlopen(path, flags) LoadLibrary(path) | |||
# define dlclose(lib) FreeLibrary((HMODULE)lib) | |||
# define dlsym GetProcAddress | |||
# ifdef _MSC_VER | |||
# define __func__ __FUNCTION__ | |||
# define INFINITY DBL_MAX + DBL_MAX | |||
# define NAN INFINITY - INFINITY | |||
# define snprintf _snprintf | |||
# endif | |||
static inline char* dlerror(void) { return "Unknown error"; } | |||
#else | |||
# include <dlfcn.h> | |||
#endif | |||
#include "serd/serd.h" | |||
#include "sord/sord.h" | |||
#include "zix/tree.h" | |||
#include "lilv_config.h" | |||
#include "lilv/lilv.h" | |||
#ifdef LILV_DYN_MANIFEST | |||
# include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h" | |||
#endif | |||
/* | |||
* | |||
* Types | |||
* | |||
*/ | |||
typedef struct LilvSpecImpl LilvSpec; | |||
typedef void LilvCollection; | |||
struct LilvPortImpl { | |||
SordNode* node; ///< RDF node | |||
uint32_t index; ///< lv2:index | |||
LilvNode* symbol; ///< lv2:symbol | |||
LilvNodes* classes; ///< rdf:type | |||
}; | |||
struct LilvSpecImpl { | |||
SordNode* spec; | |||
SordNode* bundle; | |||
LilvNodes* data_uris; | |||
struct LilvSpecImpl* next; | |||
}; | |||
/** | |||
Header of an LilvPlugin, LilvPluginClass, or LilvUI. | |||
Any of these structs may be safely casted to LilvHeader, which is used to | |||
implement collections using the same comparator. | |||
*/ | |||
struct LilvHeader { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
}; | |||
#ifdef LILV_DYN_MANIFEST | |||
typedef struct { | |||
LilvNode* bundle; | |||
void* lib; | |||
LV2_Dyn_Manifest_Handle handle; | |||
uint32_t refs; | |||
} LilvDynManifest; | |||
#endif | |||
typedef struct { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
void* lib; | |||
LV2_Descriptor_Function lv2_descriptor; | |||
#ifdef LILV_NEW_LV2 | |||
const LV2_Lib_Descriptor* desc; | |||
#endif | |||
uint32_t refs; | |||
} LilvLib; | |||
struct LilvPluginImpl { | |||
LilvWorld* world; | |||
LilvNode* plugin_uri; | |||
LilvNode* bundle_uri; ///< Bundle plugin was loaded from | |||
LilvNode* binary_uri; ///< lv2:binary | |||
#ifdef LILV_DYN_MANIFEST | |||
LilvDynManifest* dynmanifest; | |||
#endif | |||
const LilvPluginClass* plugin_class; | |||
LilvNodes* data_uris; ///< rdfs::seeAlso | |||
LilvPort** ports; | |||
uint32_t num_ports; | |||
bool loaded; | |||
bool replaced; | |||
}; | |||
struct LilvPluginClassImpl { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
LilvNode* parent_uri; | |||
LilvNode* label; | |||
}; | |||
struct LilvInstancePimpl { | |||
LilvWorld* world; | |||
LilvLib* lib; | |||
}; | |||
typedef struct { | |||
bool dyn_manifest; | |||
bool filter_language; | |||
} LilvOptions; | |||
struct LilvWorldImpl { | |||
SordWorld* world; | |||
SordModel* model; | |||
SerdReader* reader; | |||
unsigned n_read_files; | |||
LilvPluginClass* lv2_plugin_class; | |||
LilvPluginClasses* plugin_classes; | |||
LilvSpec* specs; | |||
LilvPlugins* plugins; | |||
LilvNodes* loaded_files; | |||
ZixTree* libs; | |||
struct { | |||
SordNode* dc_replaces; | |||
SordNode* dman_DynManifest; | |||
SordNode* doap_name; | |||
SordNode* lv2_Plugin; | |||
SordNode* lv2_Specification; | |||
SordNode* lv2_appliesTo; | |||
SordNode* lv2_binary; | |||
SordNode* lv2_default; | |||
SordNode* lv2_designation; | |||
SordNode* lv2_extensionData; | |||
SordNode* lv2_index; | |||
SordNode* lv2_maximum; | |||
SordNode* lv2_minimum; | |||
SordNode* lv2_name; | |||
SordNode* lv2_optionalFeature; | |||
SordNode* lv2_port; | |||
SordNode* lv2_portProperty; | |||
SordNode* lv2_reportsLatency; | |||
SordNode* lv2_requiredFeature; | |||
SordNode* lv2_symbol; | |||
SordNode* null_uri; | |||
SordNode* pset_value; | |||
SordNode* rdf_a; | |||
SordNode* rdf_value; | |||
SordNode* rdfs_Class; | |||
SordNode* rdfs_label; | |||
SordNode* rdfs_seeAlso; | |||
SordNode* rdfs_subClassOf; | |||
SordNode* xsd_base64Binary; | |||
SordNode* xsd_boolean; | |||
SordNode* xsd_decimal; | |||
SordNode* xsd_double; | |||
SordNode* xsd_integer; | |||
} uris; | |||
LilvOptions opt; | |||
}; | |||
typedef enum { | |||
LILV_VALUE_URI, | |||
LILV_VALUE_STRING, | |||
LILV_VALUE_INT, | |||
LILV_VALUE_FLOAT, | |||
LILV_VALUE_BOOL, | |||
LILV_VALUE_BLANK, | |||
LILV_VALUE_BLOB | |||
} LilvNodeType; | |||
struct LilvNodeImpl { | |||
LilvWorld* world; | |||
SordNode* node; | |||
LilvNodeType type; | |||
union { | |||
int int_val; | |||
float float_val; | |||
bool bool_val; | |||
} val; | |||
}; | |||
struct LilvScalePointImpl { | |||
LilvNode* value; | |||
LilvNode* label; | |||
}; | |||
struct LilvUIImpl { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
LilvNode* bundle_uri; | |||
LilvNode* binary_uri; | |||
LilvNodes* classes; | |||
}; | |||
/* | |||
* | |||
* Functions | |||
* | |||
*/ | |||
LilvPort* lilv_port_new(LilvWorld* world, | |||
const SordNode* node, | |||
uint32_t index, | |||
const char* symbol); | |||
void lilv_port_free(const LilvPlugin* plugin, LilvPort* port); | |||
LilvPlugin* lilv_plugin_new(LilvWorld* world, | |||
LilvNode* uri, | |||
LilvNode* bundle_uri); | |||
void lilv_plugin_load_if_necessary(const LilvPlugin* p); | |||
void lilv_plugin_free(LilvPlugin* plugin); | |||
LilvNode* lilv_plugin_get_unique(const LilvPlugin* p, | |||
const SordNode* subject, | |||
const SordNode* predicate); | |||
void lilv_collection_free(LilvCollection* collection); | |||
unsigned lilv_collection_size(const LilvCollection* collection); | |||
LilvIter* lilv_collection_begin(const LilvCollection* collection); | |||
void* lilv_collection_get(const LilvCollection* collection, | |||
const LilvIter* i); | |||
LilvPluginClass* lilv_plugin_class_new(LilvWorld* world, | |||
const SordNode* parent_uri, | |||
const SordNode* uri, | |||
const char* label); | |||
void lilv_plugin_class_free(LilvPluginClass* plugin_class); | |||
LilvLib* | |||
lilv_lib_open(LilvWorld* world, | |||
const LilvNode* uri, | |||
const char* bundle_path, | |||
const LV2_Feature*const* features); | |||
const LV2_Descriptor* lilv_lib_get_plugin(LilvLib* lib, uint32_t index); | |||
void lilv_lib_close(LilvLib* lib); | |||
LilvNodes* lilv_nodes_new(void); | |||
LilvPlugins* lilv_plugins_new(void); | |||
LilvScalePoints* lilv_scale_points_new(void); | |||
LilvPluginClasses* lilv_plugin_classes_new(void); | |||
LilvUIs* lilv_uis_new(void); | |||
const uint8_t* lilv_world_blank_node_prefix(LilvWorld* world); | |||
void lilv_world_load_file(LilvWorld* world, const char* file_uri); | |||
LilvUI* lilv_ui_new(LilvWorld* world, | |||
LilvNode* uri, | |||
LilvNode* type_uri, | |||
LilvNode* binary_uri); | |||
void lilv_ui_free(LilvUI* ui); | |||
LilvNode* lilv_node_new(LilvWorld* world, | |||
LilvNodeType type, | |||
const char* val); | |||
LilvNode* lilv_node_new_from_node(LilvWorld* world, | |||
const SordNode* node); | |||
const SordNode* lilv_node_as_node(const LilvNode* value); | |||
int lilv_header_compare_by_uri(const void* a, const void* b, void* user_data); | |||
int | |||
lilv_ptr_cmp(const void* a, const void* b, void* user_data); | |||
int | |||
lilv_resource_node_cmp(const void* a, const void* b, void* user_data); | |||
struct LilvHeader* | |||
lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri); | |||
LilvScalePoint* lilv_scale_point_new(LilvNode* value, LilvNode* label); | |||
void lilv_scale_point_free(LilvScalePoint* point); | |||
SordIter* | |||
lilv_world_query_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object); | |||
LilvNodes* | |||
lilv_world_query_values_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object); | |||
#define FOREACH_MATCH(iter) \ | |||
for (; !sord_iter_end(iter); sord_iter_next(iter)) | |||
LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* w, | |||
SordIter* stream, | |||
SordQuadIndex field); | |||
char* lilv_strjoin(const char* first, ...); | |||
char* lilv_strdup(const char* str); | |||
char* lilv_get_lang(void); | |||
char* lilv_expand(const char* path); | |||
char* lilv_dirname(const char* path); | |||
int lilv_copy_file(const char* src, const char* dst); | |||
bool lilv_path_exists(const char* path, void* ignored); | |||
char* lilv_path_absolute(const char* path); | |||
bool lilv_path_is_absolute(const char* path); | |||
char* lilv_get_latest_copy(const char* path, const char* copy_path); | |||
char* lilv_path_relative_to(const char* path, const char* base); | |||
bool lilv_path_is_child(const char* path, const char* dir); | |||
int lilv_flock(FILE* file, bool lock); | |||
char* lilv_realpath(const char* path); | |||
int lilv_symlink(const char* oldpath, const char* newpath); | |||
int lilv_mkdir_p(const char* path); | |||
char* lilv_path_join(const char* a, const char* b); | |||
bool lilv_file_equals(const char* a_path, const char* b_path); | |||
char* | |||
lilv_find_free_path(const char* in_path, | |||
bool (*exists)(const char*, void*), void* user_data); | |||
void | |||
lilv_dir_for_each(const char* path, | |||
void* data, | |||
void (*f)(const char* path, const char* name, void* data)); | |||
typedef void (*VoidFunc)(void); | |||
/** dlsym wrapper to return a function pointer (without annoying warning) */ | |||
static inline VoidFunc | |||
lilv_dlfunc(void* handle, const char* symbol) | |||
{ | |||
typedef VoidFunc (*VoidFuncGetter)(void*, const char*); | |||
VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym; | |||
return dlfunc(handle, symbol); | |||
} | |||
#ifdef LILV_DYN_MANIFEST | |||
static const LV2_Feature* const dman_features = { NULL }; | |||
#endif | |||
#define LILV_ERROR(str) fprintf(stderr, "%s(): error: " str, \ | |||
__func__) | |||
#define LILV_ERRORF(fmt, ...) fprintf(stderr, "%s(): error: " fmt, \ | |||
__func__, __VA_ARGS__) | |||
#define LILV_WARN(str) fprintf(stderr, "%s(): warning: " str, \ | |||
__func__) | |||
#define LILV_WARNF(fmt, ...) fprintf(stderr, "%s(): warning: " fmt, \ | |||
__func__, __VA_ARGS__) | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* LILV_INTERNAL_H */ |
@@ -0,0 +1,396 @@ | |||
/* | |||
Copyright 2007-2011 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 <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
static void | |||
lilv_node_set_numerics_from_string(LilvNode* val, size_t len) | |||
{ | |||
const char* str = (const char*)sord_node_get_string(val->node); | |||
char* endptr; | |||
switch (val->type) { | |||
case LILV_VALUE_URI: | |||
case LILV_VALUE_BLANK: | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_BLOB: | |||
break; | |||
case LILV_VALUE_INT: | |||
val->val.int_val = strtol(str, &endptr, 10); | |||
break; | |||
case LILV_VALUE_FLOAT: | |||
val->val.float_val = serd_strtod(str, &endptr); | |||
break; | |||
case LILV_VALUE_BOOL: | |||
val->val.bool_val = !strcmp(str, "true"); | |||
break; | |||
} | |||
} | |||
/** Note that if @a type is numeric or boolean, the returned value is corrupt | |||
* until lilv_node_set_numerics_from_string is called. It is not | |||
* automatically called from here to avoid overhead and imprecision when the | |||
* exact string value is known. | |||
*/ | |||
LilvNode* | |||
lilv_node_new(LilvWorld* world, LilvNodeType type, const char* str) | |||
{ | |||
LilvNode* val = (LilvNode*)malloc(sizeof(LilvNode)); | |||
val->world = world; | |||
val->type = type; | |||
const uint8_t* ustr = (const uint8_t*)str; | |||
switch (type) { | |||
case LILV_VALUE_URI: | |||
val->node = sord_new_uri(world->world, ustr); | |||
break; | |||
case LILV_VALUE_BLANK: | |||
val->node = sord_new_blank(world->world, ustr); | |||
break; | |||
case LILV_VALUE_STRING: | |||
val->node = sord_new_literal(world->world, NULL, ustr, NULL); | |||
break; | |||
case LILV_VALUE_INT: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_integer, ustr, NULL); | |||
break; | |||
case LILV_VALUE_FLOAT: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_decimal, ustr, NULL); | |||
break; | |||
case LILV_VALUE_BOOL: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_boolean, ustr, NULL); | |||
break; | |||
case LILV_VALUE_BLOB: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_base64Binary, ustr, NULL); | |||
break; | |||
} | |||
return val; | |||
} | |||
/** Create a new LilvNode from @a node, or return NULL if impossible */ | |||
LilvNode* | |||
lilv_node_new_from_node(LilvWorld* world, const SordNode* node) | |||
{ | |||
LilvNode* result = NULL; | |||
SordNode* datatype_uri = NULL; | |||
LilvNodeType type = LILV_VALUE_STRING; | |||
size_t len = 0; | |||
switch (sord_node_get_type(node)) { | |||
case SORD_URI: | |||
result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
result->world = (LilvWorld*)world; | |||
result->type = LILV_VALUE_URI; | |||
result->node = sord_node_copy(node); | |||
break; | |||
case SORD_BLANK: | |||
result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
result->world = (LilvWorld*)world; | |||
result->type = LILV_VALUE_BLANK; | |||
result->node = sord_node_copy(node); | |||
break; | |||
case SORD_LITERAL: | |||
datatype_uri = sord_node_get_datatype(node); | |||
if (datatype_uri) { | |||
if (sord_node_equals(datatype_uri, world->uris.xsd_boolean)) | |||
type = LILV_VALUE_BOOL; | |||
else if (sord_node_equals(datatype_uri, world->uris.xsd_decimal) | |||
|| sord_node_equals(datatype_uri, world->uris.xsd_double)) | |||
type = LILV_VALUE_FLOAT; | |||
else if (sord_node_equals(datatype_uri, world->uris.xsd_integer)) | |||
type = LILV_VALUE_INT; | |||
else if (sord_node_equals(datatype_uri, | |||
world->uris.xsd_base64Binary)) | |||
type = LILV_VALUE_BLOB; | |||
else | |||
LILV_ERRORF("Unknown datatype `%s'\n", | |||
sord_node_get_string(datatype_uri)); | |||
} | |||
result = lilv_node_new( | |||
world, type, (const char*)sord_node_get_string_counted(node, &len)); | |||
lilv_node_set_numerics_from_string(result, len); | |||
break; | |||
default: | |||
assert(false); | |||
} | |||
return result; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_uri(LilvWorld* world, const char* uri) | |||
{ | |||
return lilv_node_new(world, LILV_VALUE_URI, uri); | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_string(LilvWorld* world, const char* str) | |||
{ | |||
return lilv_node_new(world, LILV_VALUE_STRING, str); | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_int(LilvWorld* world, int val) | |||
{ | |||
char str[32]; | |||
snprintf(str, sizeof(str), "%d", val); | |||
LilvNode* ret = lilv_node_new(world, LILV_VALUE_INT, str); | |||
ret->val.int_val = val; | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_float(LilvWorld* world, float val) | |||
{ | |||
char str[32]; | |||
snprintf(str, sizeof(str), "%f", val); | |||
LilvNode* ret = lilv_node_new(world, LILV_VALUE_FLOAT, str); | |||
ret->val.float_val = val; | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_bool(LilvWorld* world, bool val) | |||
{ | |||
LilvNode* ret = lilv_node_new(world, LILV_VALUE_BOOL, | |||
val ? "true" : "false"); | |||
ret->val.bool_val = val; | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_node_duplicate(const LilvNode* val) | |||
{ | |||
if (!val) { | |||
return NULL; | |||
} | |||
LilvNode* result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
result->world = val->world; | |||
result->node = sord_node_copy(val->node); | |||
result->val = val->val; | |||
result->type = val->type; | |||
return result; | |||
} | |||
LILV_API | |||
void | |||
lilv_node_free(LilvNode* val) | |||
{ | |||
if (val) { | |||
sord_node_free(val->world->world, val->node); | |||
free(val); | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_equals(const LilvNode* value, const LilvNode* other) | |||
{ | |||
if (value == NULL && other == NULL) | |||
return true; | |||
else if (value == NULL || other == NULL) | |||
return false; | |||
else if (value->type != other->type) | |||
return false; | |||
switch (value->type) { | |||
case LILV_VALUE_URI: | |||
case LILV_VALUE_BLANK: | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_BLOB: | |||
return sord_node_equals(value->node, other->node); | |||
case LILV_VALUE_INT: | |||
return (value->val.int_val == other->val.int_val); | |||
case LILV_VALUE_FLOAT: | |||
return (value->val.float_val == other->val.float_val); | |||
case LILV_VALUE_BOOL: | |||
return (value->val.bool_val == other->val.bool_val); | |||
} | |||
return false; /* shouldn't get here */ | |||
} | |||
LILV_API | |||
char* | |||
lilv_node_get_turtle_token(const LilvNode* value) | |||
{ | |||
const char* str = (const char*)sord_node_get_string(value->node); | |||
size_t len = 0; | |||
char* result = NULL; | |||
SerdNode node; | |||
switch (value->type) { | |||
case LILV_VALUE_URI: | |||
len = strlen(str) + 3; | |||
result = (char*)calloc(len, 1); | |||
snprintf(result, len, "<%s>", str); | |||
break; | |||
case LILV_VALUE_BLANK: | |||
len = strlen(str) + 3; | |||
result = (char*)calloc(len, 1); | |||
snprintf(result, len, "_:%s", str); | |||
break; | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_BOOL: | |||
case LILV_VALUE_BLOB: | |||
result = lilv_strdup(str); | |||
break; | |||
case LILV_VALUE_INT: | |||
node = serd_node_new_integer(value->val.int_val); | |||
result = (char*)node.buf; | |||
break; | |||
case LILV_VALUE_FLOAT: | |||
node = serd_node_new_decimal(value->val.float_val, 8); | |||
result = (char*)node.buf; | |||
break; | |||
} | |||
return result; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_uri(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_URI); | |||
} | |||
LILV_API | |||
const char* | |||
lilv_node_as_uri(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_uri(value)); | |||
return (const char*)sord_node_get_string(value->node); | |||
} | |||
const SordNode* | |||
lilv_node_as_node(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_uri(value)); | |||
return value->node; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_blank(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_BLANK); | |||
} | |||
LILV_API | |||
const char* | |||
lilv_node_as_blank(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_blank(value)); | |||
return (const char*)sord_node_get_string(value->node); | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_literal(const LilvNode* value) | |||
{ | |||
if (!value) | |||
return false; | |||
switch (value->type) { | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_INT: | |||
case LILV_VALUE_FLOAT: | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_string(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_STRING); | |||
} | |||
LILV_API | |||
const char* | |||
lilv_node_as_string(const LilvNode* value) | |||
{ | |||
return value ? (const char*)sord_node_get_string(value->node) : NULL; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_int(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_INT); | |||
} | |||
LILV_API | |||
int | |||
lilv_node_as_int(const LilvNode* value) | |||
{ | |||
assert(value); | |||
assert(lilv_node_is_int(value)); | |||
return value->val.int_val; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_float(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_FLOAT); | |||
} | |||
LILV_API | |||
float | |||
lilv_node_as_float(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_float(value) || lilv_node_is_int(value)); | |||
if (lilv_node_is_float(value)) { | |||
return value->val.float_val; | |||
} else { // lilv_node_is_int(value) | |||
return (float)value->val.int_val; | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_bool(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_BOOL); | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_as_bool(const LilvNode* value) | |||
{ | |||
assert(value); | |||
assert(lilv_node_is_bool(value)); | |||
return value->val.bool_val; | |||
} |
@@ -0,0 +1,96 @@ | |||
/* | |||
Copyright 2007-2011 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 <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
LilvPluginClass* | |||
lilv_plugin_class_new(LilvWorld* world, | |||
const SordNode* parent_node, | |||
const SordNode* uri, | |||
const char* label) | |||
{ | |||
if (parent_node && sord_node_get_type(parent_node) != SORD_URI) { | |||
return NULL; // Not an LV2 plugin superclass (FIXME: discover properly) | |||
} | |||
LilvPluginClass* pc = (LilvPluginClass*)malloc(sizeof(LilvPluginClass)); | |||
pc->world = world; | |||
pc->uri = lilv_node_new_from_node(world, uri); | |||
pc->label = lilv_node_new(world, LILV_VALUE_STRING, label); | |||
pc->parent_uri = (parent_node) | |||
? lilv_node_new_from_node(world, parent_node) | |||
: NULL; | |||
return pc; | |||
} | |||
void | |||
lilv_plugin_class_free(LilvPluginClass* plugin_class) | |||
{ | |||
assert(plugin_class->uri); | |||
lilv_node_free(plugin_class->uri); | |||
lilv_node_free(plugin_class->parent_uri); | |||
lilv_node_free(plugin_class->label); | |||
free(plugin_class); | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_plugin_class_get_parent_uri(const LilvPluginClass* plugin_class) | |||
{ | |||
if (plugin_class->parent_uri) | |||
return plugin_class->parent_uri; | |||
else | |||
return NULL; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class) | |||
{ | |||
assert(plugin_class->uri); | |||
return plugin_class->uri; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_plugin_class_get_label(const LilvPluginClass* plugin_class) | |||
{ | |||
return plugin_class->label; | |||
} | |||
LILV_API | |||
LilvPluginClasses* | |||
lilv_plugin_class_get_children(const LilvPluginClass* plugin_class) | |||
{ | |||
// Returned list doesn't own categories | |||
LilvPluginClasses* all = plugin_class->world->plugin_classes; | |||
LilvPluginClasses* result = zix_tree_new(false, lilv_ptr_cmp, NULL, NULL); | |||
for (ZixTreeIter* i = zix_tree_begin((ZixTree*)all); | |||
i != zix_tree_end((ZixTree*)all); | |||
i = zix_tree_iter_next(i)) { | |||
const LilvPluginClass* c = (LilvPluginClass*)zix_tree_get(i); | |||
const LilvNode* parent = lilv_plugin_class_get_parent_uri(c); | |||
if (parent && lilv_node_equals(lilv_plugin_class_get_uri(plugin_class), | |||
parent)) | |||
zix_tree_insert((ZixTree*)result, (LilvPluginClass*)c, NULL); | |||
} | |||
return result; | |||
} |
@@ -0,0 +1,264 @@ | |||
/* | |||
Copyright 2007-2011 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 <assert.h> | |||
#include <limits.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/event.h" | |||
#include "lilv_internal.h" | |||
LilvPort* | |||
lilv_port_new(LilvWorld* world, | |||
const SordNode* node, | |||
uint32_t index, | |||
const char* symbol) | |||
{ | |||
LilvPort* port = (LilvPort*)malloc(sizeof(LilvPort)); | |||
port->node = sord_node_copy(node); | |||
port->index = index; | |||
port->symbol = lilv_node_new(world, LILV_VALUE_STRING, symbol); | |||
port->classes = lilv_nodes_new(); | |||
return port; | |||
} | |||
void | |||
lilv_port_free(const LilvPlugin* plugin, LilvPort* port) | |||
{ | |||
if (port) { | |||
sord_node_free(plugin->world->world, port->node); | |||
lilv_nodes_free(port->classes); | |||
lilv_node_free(port->symbol); | |||
free(port); | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_port_is_a(const LilvPlugin* plugin, | |||
const LilvPort* port, | |||
const LilvNode* port_class) | |||
{ | |||
LILV_FOREACH(nodes, i, port->classes) | |||
if (lilv_node_equals(lilv_nodes_get(port->classes, i), port_class)) | |||
return true; | |||
return false; | |||
} | |||
LILV_API | |||
bool | |||
lilv_port_has_property(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const LilvNode* property) | |||
{ | |||
assert(property); | |||
SordIter* results = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
p->world->uris.lv2_portProperty, | |||
lilv_node_as_node(property)); | |||
const bool ret = !sord_iter_end(results); | |||
sord_iter_free(results); | |||
return ret; | |||
} | |||
LILV_API | |||
bool | |||
lilv_port_supports_event(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const LilvNode* event) | |||
{ | |||
assert(event); | |||
SordIter* results = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
sord_new_uri(p->world->world, (const uint8_t*)LV2_EVENT__supportsEvent), | |||
lilv_node_as_node(event)); | |||
const bool ret = !sord_iter_end(results); | |||
sord_iter_free(results); | |||
return ret; | |||
} | |||
static LilvNodes* | |||
lilv_port_get_value_by_node(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const SordNode* predicate) | |||
{ | |||
assert(sord_node_get_type(predicate) == SORD_URI); | |||
SordIter* results = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
predicate, | |||
NULL); | |||
return lilv_nodes_from_stream_objects(p->world, results, SORD_OBJECT); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_port_get_value(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const LilvNode* predicate) | |||
{ | |||
if (!lilv_node_is_uri(predicate)) { | |||
LILV_ERRORF("Predicate `%s' is not a URI\n", | |||
sord_node_get_string(predicate->node)); | |||
return NULL; | |||
} | |||
return lilv_port_get_value_by_node( | |||
p, port, | |||
lilv_node_as_node(predicate)); | |||
} | |||
LILV_API | |||
uint32_t | |||
lilv_port_get_index(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
return port->index; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_port_get_symbol(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
return port->symbol; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_port_get_name(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
LilvNodes* results = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_name); | |||
LilvNode* ret = NULL; | |||
if (results) { | |||
LilvNode* val = lilv_nodes_get_first(results); | |||
if (lilv_node_is_string(val)) | |||
ret = lilv_node_duplicate(val); | |||
lilv_nodes_free(results); | |||
} | |||
if (!ret) | |||
LILV_WARNF("Plugin <%s> port has no (mandatory) doap:name\n", | |||
lilv_node_as_string(lilv_plugin_get_uri(p))); | |||
return ret; | |||
} | |||
LILV_API | |||
const LilvNodes* | |||
lilv_port_get_classes(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
return port->classes; | |||
} | |||
LILV_API | |||
void | |||
lilv_port_get_range(const LilvPlugin* p, | |||
const LilvPort* port, | |||
LilvNode** def, | |||
LilvNode** min, | |||
LilvNode** max) | |||
{ | |||
if (def) { | |||
LilvNodes* defaults = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_default); | |||
*def = defaults | |||
? lilv_node_duplicate(lilv_nodes_get_first(defaults)) | |||
: NULL; | |||
lilv_nodes_free(defaults); | |||
} | |||
if (min) { | |||
LilvNodes* minimums = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_minimum); | |||
*min = minimums | |||
? lilv_node_duplicate(lilv_nodes_get_first(minimums)) | |||
: NULL; | |||
lilv_nodes_free(minimums); | |||
} | |||
if (max) { | |||
LilvNodes* maximums = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_maximum); | |||
*max = maximums | |||
? lilv_node_duplicate(lilv_nodes_get_first(maximums)) | |||
: NULL; | |||
lilv_nodes_free(maximums); | |||
} | |||
} | |||
LILV_API | |||
LilvScalePoints* | |||
lilv_port_get_scale_points(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
SordIter* points = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
sord_new_uri(p->world->world, (const uint8_t*)LV2_CORE__scalePoint), | |||
NULL); | |||
LilvScalePoints* ret = NULL; | |||
if (!sord_iter_end(points)) | |||
ret = lilv_scale_points_new(); | |||
FOREACH_MATCH(points) { | |||
const SordNode* point = sord_iter_get_node(points, SORD_OBJECT); | |||
LilvNode* value = lilv_plugin_get_unique( | |||
p, | |||
point, | |||
p->world->uris.rdf_value); | |||
LilvNode* label = lilv_plugin_get_unique( | |||
p, | |||
point, | |||
p->world->uris.rdfs_label); | |||
if (value && label) { | |||
zix_tree_insert( | |||
(ZixTree*)ret, lilv_scale_point_new(value, label), NULL); | |||
} | |||
} | |||
sord_iter_free(points); | |||
assert(!ret || lilv_nodes_size(ret) > 0); | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_port_get_properties(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
LilvNode* pred = lilv_node_new_from_node( | |||
p->world, p->world->uris.lv2_portProperty); | |||
LilvNodes* ret = lilv_port_get_value(p, port, pred); | |||
lilv_node_free(pred); | |||
return ret; | |||
} |
@@ -0,0 +1,140 @@ | |||
/* | |||
Copyright 2007-2011 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 <assert.h> | |||
#include <limits.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
typedef enum { | |||
LILV_LANG_MATCH_NONE, ///< Language does not match at all | |||
LILV_LANG_MATCH_PARTIAL, ///< Partial (language, but not country) match | |||
LILV_LANG_MATCH_EXACT ///< Exact (language and country) match | |||
} LilvLangMatch; | |||
static LilvLangMatch | |||
lilv_lang_matches(const char* a, const char* b) | |||
{ | |||
if (!strcmp(a, b)) { | |||
return LILV_LANG_MATCH_EXACT; | |||
} | |||
const char* a_dash = strchr(a, '-'); | |||
const size_t a_lang_len = a_dash ? (size_t)(a_dash - a) : strlen(a); | |||
const char* b_dash = strchr(b, '-'); | |||
const size_t b_lang_len = b_dash ? (size_t)(b_dash - b) : strlen(b); | |||
if (a_lang_len == b_lang_len && !strncmp(a, b, a_lang_len)) { | |||
return LILV_LANG_MATCH_PARTIAL; | |||
} | |||
return LILV_LANG_MATCH_NONE; | |||
} | |||
static LilvNodes* | |||
lilv_nodes_from_stream_objects_i18n(LilvWorld* world, | |||
SordIter* stream, | |||
SordQuadIndex field) | |||
{ | |||
LilvNodes* values = lilv_nodes_new(); | |||
const SordNode* nolang = NULL; // Untranslated value | |||
const SordNode* partial = NULL; // Partial language match | |||
char* syslang = lilv_get_lang(); | |||
FOREACH_MATCH(stream) { | |||
const SordNode* value = sord_iter_get_node(stream, field); | |||
if (sord_node_get_type(value) == SORD_LITERAL) { | |||
const char* lang = sord_node_get_language(value); | |||
LilvLangMatch lm = LILV_LANG_MATCH_NONE; | |||
if (lang) { | |||
lm = (syslang) | |||
? lilv_lang_matches(lang, syslang) | |||
: LILV_LANG_MATCH_PARTIAL; | |||
} else { | |||
nolang = value; | |||
if (!syslang) { | |||
lm = LILV_LANG_MATCH_EXACT; | |||
} | |||
} | |||
if (lm == LILV_LANG_MATCH_EXACT) { | |||
// Exact language match, add to results | |||
zix_tree_insert((ZixTree*)values, | |||
lilv_node_new_from_node(world, value), | |||
NULL); | |||
} else if (lm == LILV_LANG_MATCH_PARTIAL) { | |||
// Partial language match, save in case we find no exact | |||
partial = value; | |||
} | |||
} else { | |||
zix_tree_insert((ZixTree*)values, | |||
lilv_node_new_from_node(world, value), | |||
NULL); | |||
} | |||
} | |||
sord_iter_free(stream); | |||
free(syslang); | |||
if (lilv_nodes_size(values) > 0) { | |||
return values; | |||
} | |||
const SordNode* best = nolang; | |||
if (syslang && partial) { | |||
// Partial language match for system language | |||
best = partial; | |||
} else if (!best) { | |||
// No languages matches at all, and no untranslated value | |||
// Use any value, if possible | |||
best = partial; | |||
} | |||
if (best) { | |||
zix_tree_insert( | |||
(ZixTree*)values, lilv_node_new_from_node(world, best), NULL); | |||
} else { | |||
// No matches whatsoever | |||
lilv_nodes_free(values); | |||
values = NULL; | |||
} | |||
return values; | |||
} | |||
LilvNodes* | |||
lilv_nodes_from_stream_objects(LilvWorld* world, | |||
SordIter* stream, | |||
SordQuadIndex field) | |||
{ | |||
if (sord_iter_end(stream)) { | |||
sord_iter_free(stream); | |||
return NULL; | |||
} else if (world->opt.filter_language) { | |||
return lilv_nodes_from_stream_objects_i18n(world, stream, field); | |||
} else { | |||
LilvNodes* values = lilv_nodes_new(); | |||
FOREACH_MATCH(stream) { | |||
const SordNode* value = sord_iter_get_node(stream, field); | |||
LilvNode* node = lilv_node_new_from_node(world, value); | |||
if (node) { | |||
zix_tree_insert((ZixTree*)values, node, NULL); | |||
} | |||
} | |||
sord_iter_free(stream); | |||
return values; | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
Copyright 2007-2011 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 "lilv_internal.h" | |||
/** Ownership of value and label is taken */ | |||
LilvScalePoint* | |||
lilv_scale_point_new(LilvNode* value, LilvNode* label) | |||
{ | |||
LilvScalePoint* point = (LilvScalePoint*)malloc( | |||
sizeof(struct LilvScalePointImpl)); | |||
point->value = value; | |||
point->label = label; | |||
return point; | |||
} | |||
void | |||
lilv_scale_point_free(LilvScalePoint* point) | |||
{ | |||
if (point) { | |||
lilv_node_free(point->value); | |||
lilv_node_free(point->label); | |||
free(point); | |||
} | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_scale_point_get_value(const LilvScalePoint* p) | |||
{ | |||
return p->value; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_scale_point_get_label(const LilvScalePoint* p) | |||
{ | |||
return p->label; | |||
} |
@@ -0,0 +1,184 @@ | |||
/* | |||
Copyright 2007-2011 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 <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
LilvUI* | |||
lilv_ui_new(LilvWorld* world, | |||
LilvNode* uri, | |||
LilvNode* type_uri, | |||
LilvNode* binary_uri) | |||
{ | |||
assert(uri); | |||
assert(type_uri); | |||
assert(binary_uri); | |||
LilvUI* ui = (LilvUI*)malloc(sizeof(LilvUI)); | |||
ui->world = world; | |||
ui->uri = uri; | |||
ui->binary_uri = binary_uri; | |||
// FIXME: kludge | |||
char* bundle = lilv_strdup(lilv_node_as_string(ui->binary_uri)); | |||
char* last_slash = strrchr(bundle, '/') + 1; | |||
*last_slash = '\0'; | |||
ui->bundle_uri = lilv_new_uri(world, bundle); | |||
free(bundle); | |||
ui->classes = lilv_nodes_new(); | |||
zix_tree_insert((ZixTree*)ui->classes, type_uri, NULL); | |||
return ui; | |||
} | |||
void | |||
lilv_ui_free(LilvUI* ui) | |||
{ | |||
lilv_node_free(ui->uri); | |||
ui->uri = NULL; | |||
lilv_node_free(ui->bundle_uri); | |||
ui->bundle_uri = NULL; | |||
lilv_node_free(ui->binary_uri); | |||
ui->binary_uri = NULL; | |||
lilv_nodes_free(ui->classes); | |||
free(ui); | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_ui_get_uri(const LilvUI* ui) | |||
{ | |||
assert(ui); | |||
assert(ui->uri); | |||
return ui->uri; | |||
} | |||
LILV_API | |||
unsigned | |||
lilv_ui_is_supported(const LilvUI* ui, | |||
LilvUISupportedFunc supported_func, | |||
const LilvNode* container_type, | |||
const LilvNode** ui_type) | |||
{ | |||
const LilvNodes* classes = lilv_ui_get_classes(ui); | |||
LILV_FOREACH(nodes, c, classes) { | |||
const LilvNode* type = lilv_nodes_get(classes, c); | |||
const unsigned q = supported_func(lilv_node_as_uri(container_type), | |||
lilv_node_as_uri(type)); | |||
if (q) { | |||
if (ui_type) { | |||
*ui_type = type; | |||
} | |||
return q; | |||
} | |||
} | |||
return 0; | |||
} | |||
LILV_API | |||
const LilvNodes* | |||
lilv_ui_get_classes(const LilvUI* ui) | |||
{ | |||
return ui->classes; | |||
} | |||
LILV_API | |||
bool | |||
lilv_ui_is_a(const LilvUI* ui, const LilvNode* ui_class_uri) | |||
{ | |||
return lilv_nodes_contains(ui->classes, ui_class_uri); | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_ui_get_bundle_uri(const LilvUI* ui) | |||
{ | |||
assert(ui); | |||
assert(ui->bundle_uri); | |||
return ui->bundle_uri; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_ui_get_binary_uri(const LilvUI* ui) | |||
{ | |||
assert(ui); | |||
assert(ui->binary_uri); | |||
return ui->binary_uri; | |||
} | |||
static LilvNodes* | |||
lilv_ui_get_value_internal(const LilvUI* ui, | |||
const SordNode* predicate) | |||
{ | |||
return lilv_world_query_values_internal( | |||
ui->world, ui->uri->node, predicate, NULL); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_supported_features(const LilvUI* ui) | |||
{ | |||
LilvNodes* optional = lilv_ui_get_optional_features(ui); | |||
LilvNodes* required = lilv_ui_get_required_features(ui); | |||
LilvNodes* result = lilv_nodes_new(); | |||
LILV_FOREACH(nodes, i, optional) | |||
zix_tree_insert((ZixTree*)result, | |||
lilv_node_duplicate(lilv_nodes_get(optional, i)), | |||
NULL); | |||
LILV_FOREACH(nodes, i, required) | |||
zix_tree_insert((ZixTree*)result, | |||
lilv_node_duplicate(lilv_nodes_get(required, i)), | |||
NULL); | |||
lilv_nodes_free(optional); | |||
lilv_nodes_free(required); | |||
return result; | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_required_features(const LilvUI* ui) | |||
{ | |||
return lilv_ui_get_value_internal( | |||
ui, ui->world->uris.lv2_requiredFeature); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_optional_features(const LilvUI* ui) | |||
{ | |||
return lilv_ui_get_value_internal( | |||
ui, ui->world->uris.lv2_optionalFeature); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_extension_data(const LilvUI* ui) | |||
{ | |||
return lilv_ui_get_value_internal(ui, ui->world->uris.lv2_extensionData); | |||
} |
@@ -0,0 +1,604 @@ | |||
/* | |||
Copyright 2007-2011 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. | |||
*/ | |||
#define _POSIX_C_SOURCE 1 /* for fileno */ | |||
#define _BSD_SOURCE 1 /* for realpath, symlink */ | |||
#ifdef __APPLE__ | |||
# define _DARWIN_C_SOURCE 1 /* for flock */ | |||
#endif | |||
#include <assert.h> | |||
#include <ctype.h> | |||
#include <errno.h> | |||
#include <stdarg.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#ifdef _WIN32 | |||
# include <windows.h> | |||
# include <direct.h> | |||
# include <io.h> | |||
# define F_OK 0 | |||
# define mkdir(path, flags) _mkdir(path) | |||
#else | |||
# include <dirent.h> | |||
# include <unistd.h> | |||
#endif | |||
#include <sys/stat.h> | |||
#include <sys/types.h> | |||
#include "lilv_internal.h" | |||
#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) | |||
# include <sys/file.h> | |||
#endif | |||
#ifndef PAGE_SIZE | |||
# define PAGE_SIZE 4096 | |||
#endif | |||
char* | |||
lilv_strjoin(const char* first, ...) | |||
{ | |||
size_t len = strlen(first); | |||
char* result = (char*)malloc(len + 1); | |||
memcpy(result, first, len); | |||
va_list args; | |||
va_start(args, first); | |||
while (1) { | |||
const char* const s = va_arg(args, const char *); | |||
if (s == NULL) | |||
break; | |||
const size_t this_len = strlen(s); | |||
if (!(result = (char*)realloc(result, len + this_len + 1))) { | |||
free(result); | |||
LILV_ERROR("realloc() failed\n"); | |||
return NULL; | |||
} | |||
memcpy(result + len, s, this_len); | |||
len += this_len; | |||
} | |||
va_end(args); | |||
result[len] = '\0'; | |||
return result; | |||
} | |||
char* | |||
lilv_strdup(const char* str) | |||
{ | |||
if (!str) { | |||
return NULL; | |||
} | |||
const size_t len = strlen(str); | |||
char* dup = (char*)malloc(len + 1); | |||
memcpy(dup, str, len + 1); | |||
return dup; | |||
} | |||
const char* | |||
lilv_uri_to_path(const char* uri) | |||
{ | |||
return (const char*)serd_uri_to_path((const uint8_t*)uri); | |||
} | |||
/** Return the current LANG converted to Turtle (i.e. RFC3066) style. | |||
* For example, if LANG is set to "en_CA.utf-8", this returns "en-ca". | |||
*/ | |||
char* | |||
lilv_get_lang(void) | |||
{ | |||
const char* const env_lang = getenv("LANG"); | |||
if (!env_lang || !strcmp(env_lang, "") | |||
|| !strcmp(env_lang, "C") || !strcmp(env_lang, "POSIX")) { | |||
return NULL; | |||
} | |||
const size_t env_lang_len = strlen(env_lang); | |||
char* const lang = (char*)malloc(env_lang_len + 1); | |||
for (size_t i = 0; i < env_lang_len + 1; ++i) { | |||
if (env_lang[i] == '_') { | |||
lang[i] = '-'; // Convert _ to - | |||
} else if (env_lang[i] >= 'A' && env_lang[i] <= 'Z') { | |||
lang[i] = env_lang[i] + ('a' - 'A'); // Convert to lowercase | |||
} else if (env_lang[i] >= 'a' && env_lang[i] <= 'z') { | |||
lang[i] = env_lang[i]; // Lowercase letter, copy verbatim | |||
} else if (env_lang[i] >= '0' && env_lang[i] <= '9') { | |||
lang[i] = env_lang[i]; // Digit, copy verbatim | |||
} else if (env_lang[i] == '\0' || env_lang[i] == '.') { | |||
// End, or start of suffix (e.g. en_CA.utf-8), finished | |||
lang[i] = '\0'; | |||
break; | |||
} else { | |||
LILV_ERRORF("Illegal LANG `%s' ignored\n", env_lang); | |||
free(lang); | |||
return NULL; | |||
} | |||
} | |||
return lang; | |||
} | |||
/** Append suffix to dst, update dst_len, and return the realloc'd result. */ | |||
static char* | |||
strappend(char* dst, size_t* dst_len, const char* suffix, size_t suffix_len) | |||
{ | |||
dst = (char*)realloc(dst, *dst_len + suffix_len + 1); | |||
memcpy(dst + *dst_len, suffix, suffix_len); | |||
dst[(*dst_len += suffix_len)] = '\0'; | |||
return dst; | |||
} | |||
/** Append the value of the environment variable var to dst. */ | |||
static char* | |||
append_var(char* dst, size_t* dst_len, const char* var) | |||
{ | |||
// Get value from environment | |||
const char* val = getenv(var); | |||
if (val) { // Value found, append it | |||
return strappend(dst, dst_len, val, strlen(val)); | |||
} else { // No value found, append variable reference as-is | |||
return strappend(strappend(dst, dst_len, "$", 1), | |||
dst_len, var, strlen(var)); | |||
} | |||
} | |||
/** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */ | |||
char* | |||
lilv_expand(const char* path) | |||
{ | |||
#ifdef _WIN32 | |||
char* out = (char*)malloc(MAX_PATH); | |||
ExpandEnvironmentStrings(path, out, MAX_PATH); | |||
#else | |||
char* out = NULL; | |||
size_t len = 0; | |||
const char* start = path; // Start of current chunk to copy | |||
for (const char* s = path; *s;) { | |||
if (*s == '$') { | |||
// Hit $ (variable reference, e.g. $VAR_NAME) | |||
for (const char* t = s + 1; ; ++t) { | |||
if (!*t || (!isupper(*t) && !isdigit(*t) && *t != '_')) { | |||
// Append preceding chunk | |||
out = strappend(out, &len, start, s - start); | |||
// Append variable value (or $VAR_NAME if not found) | |||
char* var = (char*)calloc(t - s, 1); | |||
memcpy(var, s + 1, t - s - 1); | |||
out = append_var(out, &len, var); | |||
free(var); | |||
// Continue after variable reference | |||
start = s = t; | |||
break; | |||
} | |||
} | |||
} else if (*s == '~' && (*(s + 1) == '/' || !*(s + 1))) { | |||
// Hit ~ before slash or end of string (home directory reference) | |||
out = strappend(out, &len, start, s - start); | |||
out = append_var(out, &len, "HOME"); | |||
start = ++s; | |||
} else { | |||
++s; | |||
} | |||
} | |||
if (*start) { | |||
out = strappend(out, &len, start, strlen(start)); | |||
} | |||
#endif | |||
return out; | |||
} | |||
static bool | |||
lilv_is_dir_sep(const char c) | |||
{ | |||
return c == '/' || c == LILV_DIR_SEP[0]; | |||
} | |||
char* | |||
lilv_dirname(const char* path) | |||
{ | |||
const char* s = path + strlen(path) - 1; // Last character | |||
for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash | |||
for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash | |||
for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates | |||
if (s == path) { // Hit beginning | |||
return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); | |||
} else { // Pointing to the last character of the result (inclusive) | |||
char* dirname = (char*)malloc(s - path + 2); | |||
memcpy(dirname, path, s - path + 1); | |||
dirname[s - path + 1] = '\0'; | |||
return dirname; | |||
} | |||
} | |||
bool | |||
lilv_path_exists(const char* path, void* ignored) | |||
{ | |||
return !access(path, F_OK); | |||
} | |||
char* | |||
lilv_find_free_path(const char* in_path, | |||
bool (*exists)(const char*, void*), void* user_data) | |||
{ | |||
const size_t in_path_len = strlen(in_path); | |||
char* path = (char*)malloc(in_path_len + 7); | |||
memcpy(path, in_path, in_path_len + 1); | |||
for (int i = 2; i < 1000000; ++i) { | |||
if (!exists(path, user_data)) { | |||
return path; | |||
} | |||
snprintf(path, in_path_len + 7, "%s.%u", in_path, i); | |||
} | |||
return NULL; | |||
} | |||
int | |||
lilv_copy_file(const char* src, const char* dst) | |||
{ | |||
FILE* in = fopen(src, "r"); | |||
if (!in) { | |||
LILV_ERRORF("error opening %s (%s)\n", src, strerror(errno)); | |||
return 1; | |||
} | |||
FILE* out = fopen(dst, "w"); | |||
if (!out) { | |||
LILV_ERRORF("error opening %s (%s)\n", dst, strerror(errno)); | |||
fclose(in); | |||
return 2; | |||
} | |||
char* page = (char*)malloc(PAGE_SIZE); | |||
size_t n_read = 0; | |||
while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) { | |||
if (fwrite(page, 1, n_read, out) != n_read) { | |||
LILV_ERRORF("write to %s failed (%s)\n", dst, strerror(errno)); | |||
break; | |||
} | |||
} | |||
const int ret = ferror(in) || ferror(out); | |||
if (ferror(in)) { | |||
LILV_ERRORF("read from %s failed (%s)\n", src, strerror(errno)); | |||
} | |||
free(page); | |||
fclose(in); | |||
fclose(out); | |||
return ret; | |||
} | |||
bool | |||
lilv_path_is_absolute(const char* path) | |||
{ | |||
if (lilv_is_dir_sep(path[0])) { | |||
return true; | |||
} | |||
#ifdef _WIN32 | |||
if (isalpha(path[0]) && path[1] == ':' && lilv_is_dir_sep(path[2])) { | |||
return true; | |||
} | |||
#endif | |||
return false; | |||
} | |||
char* | |||
lilv_path_absolute(const char* path) | |||
{ | |||
if (lilv_path_is_absolute(path)) { | |||
return lilv_strdup(path); | |||
} else { | |||
char* cwd = getcwd(NULL, 0); | |||
char* abs_path = lilv_path_join(cwd, path); | |||
free(cwd); | |||
return abs_path; | |||
} | |||
} | |||
char* | |||
lilv_path_join(const char* a, const char* b) | |||
{ | |||
if (!a) { | |||
return lilv_strdup(b); | |||
} | |||
const size_t a_len = strlen(a); | |||
const size_t b_len = b ? strlen(b) : 0; | |||
const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0); | |||
char* path = (char*)calloc(1, a_len + b_len + 2); | |||
memcpy(path, a, pre_len); | |||
path[pre_len] = '/'; | |||
if (b) { | |||
memcpy(path + pre_len + 1, | |||
b + (lilv_is_dir_sep(b[0]) ? 1 : 0), | |||
lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len); | |||
} | |||
return path; | |||
} | |||
static void | |||
lilv_size_mtime(const char* path, off_t* size, time_t* time) | |||
{ | |||
struct stat buf; | |||
if (stat(path, &buf)) { | |||
LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
} | |||
if (size) { | |||
*size = buf.st_size; | |||
} | |||
if (time) { | |||
*time = buf.st_mtime; | |||
} | |||
} | |||
typedef struct { | |||
char* pattern; | |||
off_t orig_size; | |||
time_t time; | |||
char* latest; | |||
} Latest; | |||
static void | |||
update_latest(const char* path, const char* name, void* data) | |||
{ | |||
Latest* latest = (Latest*)data; | |||
char* entry_path = lilv_path_join(path, name); | |||
unsigned num; | |||
if (sscanf(entry_path, latest->pattern, &num) == 1) { | |||
off_t entry_size = 0; | |||
time_t entry_time = 0; | |||
lilv_size_mtime(entry_path, &entry_size, &entry_time); | |||
if (entry_size == latest->orig_size && entry_time >= latest->time) { | |||
free(latest->latest); | |||
latest->latest = entry_path; | |||
} | |||
} | |||
if (entry_path != latest->latest) { | |||
free(entry_path); | |||
} | |||
} | |||
/** Return the latest copy of the file at @c path that is newer. */ | |||
char* | |||
lilv_get_latest_copy(const char* path, const char* copy_path) | |||
{ | |||
char* copy_dir = lilv_dirname(copy_path); | |||
Latest latest = { lilv_strjoin(copy_path, "%u", NULL), 0, 0, NULL }; | |||
lilv_size_mtime(path, &latest.orig_size, &latest.time); | |||
lilv_dir_for_each(copy_dir, &latest, update_latest); | |||
free(latest.pattern); | |||
free(copy_dir); | |||
return latest.latest; | |||
} | |||
char* | |||
lilv_realpath(const char* path) | |||
{ | |||
#ifdef _WIN32 | |||
char* out = (char*)malloc(MAX_PATH); | |||
GetFullPathName(path, MAX_PATH, out, NULL); | |||
return out; | |||
#else | |||
char* real_path = realpath(path, NULL); | |||
return real_path ? real_path : lilv_strdup(path); | |||
#endif | |||
} | |||
int | |||
lilv_symlink(const char* oldpath, const char* newpath) | |||
{ | |||
int ret = 0; | |||
if (strcmp(oldpath, newpath)) { | |||
#ifdef _WIN32 | |||
ret = 0; | |||
#else | |||
ret = symlink(oldpath, newpath); | |||
#endif | |||
} | |||
if (ret) { | |||
LILV_ERRORF("Failed to link %s => %s (%s)\n", | |||
newpath, oldpath, strerror(errno)); | |||
} | |||
return ret; | |||
} | |||
char* | |||
lilv_path_relative_to(const char* path, const char* base) | |||
{ | |||
const size_t path_len = strlen(path); | |||
const size_t base_len = strlen(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; | |||
for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { | |||
if (lilv_is_dir_sep(path[i])) { | |||
last_shared_sep = i; | |||
} | |||
} | |||
if (last_shared_sep == 0) { | |||
// No common components, return path | |||
return lilv_strdup(path); | |||
} | |||
// Find the number of up references ("..") required | |||
size_t up = 0; | |||
for (size_t i = last_shared_sep + 1; i < base_len; ++i) { | |||
if (lilv_is_dir_sep(base[i])) { | |||
++up; | |||
} | |||
} | |||
// Write up references | |||
const size_t suffix_len = path_len - last_shared_sep; | |||
char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1); | |||
for (size_t i = 0; i < up; ++i) { | |||
memcpy(rel + (i * 3), "../", 3); | |||
} | |||
// Write suffix | |||
memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); | |||
return rel; | |||
} | |||
bool | |||
lilv_path_is_child(const char* path, const char* dir) | |||
{ | |||
if (path && dir) { | |||
const size_t path_len = strlen(path); | |||
const size_t dir_len = strlen(dir); | |||
return dir && path_len >= dir_len && !strncmp(path, dir, dir_len); | |||
} | |||
return false; | |||
} | |||
int | |||
lilv_flock(FILE* file, bool lock) | |||
{ | |||
#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) | |||
return flock(fileno(file), lock ? LOCK_EX : LOCK_UN); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void | |||
lilv_dir_for_each(const char* path, | |||
void* data, | |||
void (*f)(const char* path, const char* name, void* data)) | |||
{ | |||
#ifdef _WIN32 | |||
char* pat = lilv_path_join(path, "*"); | |||
WIN32_FIND_DATA fd; | |||
HANDLE fh = FindFirstFile(pat, &fd); | |||
if (fh != INVALID_HANDLE_VALUE) { | |||
do { | |||
f(path, fd.cFileName, data); | |||
} while (FindNextFile(fh, &fd)); | |||
} | |||
free(pat); | |||
#else | |||
DIR* dir = opendir(path); | |||
if (dir) { | |||
struct dirent entry; | |||
struct dirent* result; | |||
while (!readdir_r(dir, &entry, &result) && result) { | |||
f(path, entry.d_name, data); | |||
} | |||
closedir(dir); | |||
} | |||
#endif | |||
} | |||
int | |||
lilv_mkdir_p(const char* dir_path) | |||
{ | |||
char* path = lilv_strdup(dir_path); | |||
const size_t path_len = strlen(path); | |||
for (size_t i = 1; i <= path_len; ++i) { | |||
if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') { | |||
path[i] = '\0'; | |||
if (mkdir(path, 0755) && errno != EEXIST) { | |||
LILV_ERRORF("Failed to create %s (%s)\n", | |||
path, strerror(errno)); | |||
free(path); | |||
return 1; | |||
} | |||
path[i] = LILV_DIR_SEP[0]; | |||
} | |||
} | |||
free(path); | |||
return 0; | |||
} | |||
static off_t | |||
lilv_file_size(const char* path) | |||
{ | |||
struct stat buf; | |||
if (stat(path, &buf)) { | |||
LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
return 0; | |||
} | |||
return buf.st_size; | |||
} | |||
bool | |||
lilv_file_equals(const char* a_path, const char* b_path) | |||
{ | |||
if (!strcmp(a_path, b_path)) { | |||
return true; // Paths match | |||
} | |||
bool match = false; | |||
FILE* a_file = NULL; | |||
FILE* b_file = NULL; | |||
char* const a_real = lilv_realpath(a_path); | |||
char* const b_real = lilv_realpath(b_path); | |||
if (!a_real || !b_real) { | |||
match = false; // Missing file matches nothing | |||
} else if (!strcmp(a_real, b_real)) { | |||
match = true; // Real paths match | |||
} else if (lilv_file_size(a_path) != lilv_file_size(b_path)) { | |||
match = false; // Sizes differ | |||
} else if (!(a_file = fopen(a_real, "rb"))) { | |||
match = false; // Missing file matches nothing | |||
} else if (!(b_file = fopen(b_real, "rb"))) { | |||
match = false; // Missing file matches nothing | |||
} else { | |||
match = true; | |||
// TODO: Improve performance by reading chunks | |||
while (!feof(a_file) && !feof(b_file)) { | |||
if (fgetc(a_file) != fgetc(b_file)) { | |||
match = false; | |||
break; | |||
} | |||
} | |||
} | |||
if (a_file) { | |||
fclose(a_file); | |||
} | |||
if (b_file) { | |||
fclose(b_file); | |||
} | |||
free(a_real); | |||
free(b_real); | |||
return match; | |||
} |
@@ -0,0 +1,822 @@ | |||
/* | |||
Copyright 2007-2011 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 <assert.h> | |||
#include <errno.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/presets.h" | |||
#include "lilv_internal.h" | |||
LILV_API | |||
LilvWorld* | |||
lilv_world_new(void) | |||
{ | |||
LilvWorld* world = (LilvWorld*)malloc(sizeof(LilvWorld)); | |||
world->world = sord_world_new(); | |||
if (!world->world) | |||
goto fail; | |||
world->model = sord_new(world->world, SORD_SPO|SORD_OPS, true); | |||
if (!world->model) | |||
goto fail; | |||
world->specs = NULL; | |||
world->plugin_classes = lilv_plugin_classes_new(); | |||
world->plugins = lilv_plugins_new(); | |||
world->loaded_files = zix_tree_new( | |||
false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free); | |||
#ifdef LILV_NEW_LV2 | |||
world->libs = zix_tree_new( | |||
false, lilv_header_compare_by_uri, NULL, NULL); | |||
#endif | |||
#define NS_DCTERMS "http://purl.org/dc/terms/" | |||
#define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#" | |||
#define NEW_URI(uri) sord_new_uri(world->world, (const uint8_t*)uri) | |||
world->uris.dc_replaces = NEW_URI(NS_DCTERMS "replaces"); | |||
world->uris.dman_DynManifest = NEW_URI(NS_DYNMAN "DynManifest"); | |||
world->uris.doap_name = NEW_URI(LILV_NS_DOAP "name"); | |||
world->uris.lv2_Plugin = NEW_URI(LV2_CORE__Plugin); | |||
world->uris.lv2_Specification = NEW_URI(LV2_CORE__Specification); | |||
world->uris.lv2_appliesTo = NEW_URI(LV2_CORE__appliesTo); | |||
world->uris.lv2_binary = NEW_URI(LV2_CORE__binary); | |||
world->uris.lv2_default = NEW_URI(LV2_CORE__default); | |||
world->uris.lv2_designation = NEW_URI(LV2_CORE__designation); | |||
world->uris.lv2_extensionData = NEW_URI(LV2_CORE__extensionData); | |||
world->uris.lv2_index = NEW_URI(LV2_CORE__index); | |||
world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum); | |||
world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum); | |||
world->uris.lv2_name = NEW_URI(LV2_CORE__name); | |||
world->uris.lv2_optionalFeature = NEW_URI(LV2_CORE__optionalFeature); | |||
world->uris.lv2_port = NEW_URI(LV2_CORE__port); | |||
world->uris.lv2_portProperty = NEW_URI(LV2_CORE__portProperty); | |||
world->uris.lv2_reportsLatency = NEW_URI(LV2_CORE__reportsLatency); | |||
world->uris.lv2_requiredFeature = NEW_URI(LV2_CORE__requiredFeature); | |||
world->uris.lv2_symbol = NEW_URI(LV2_CORE__symbol); | |||
world->uris.pset_value = NEW_URI(LV2_PRESETS__value); | |||
world->uris.rdf_a = NEW_URI(LILV_NS_RDF "type"); | |||
world->uris.rdf_value = NEW_URI(LILV_NS_RDF "value"); | |||
world->uris.rdfs_Class = NEW_URI(LILV_NS_RDFS "Class"); | |||
world->uris.rdfs_label = NEW_URI(LILV_NS_RDFS "label"); | |||
world->uris.rdfs_seeAlso = NEW_URI(LILV_NS_RDFS "seeAlso"); | |||
world->uris.rdfs_subClassOf = NEW_URI(LILV_NS_RDFS "subClassOf"); | |||
world->uris.xsd_base64Binary = NEW_URI(LILV_NS_XSD "base64Binary"); | |||
world->uris.xsd_boolean = NEW_URI(LILV_NS_XSD "boolean"); | |||
world->uris.xsd_decimal = NEW_URI(LILV_NS_XSD "decimal"); | |||
world->uris.xsd_double = NEW_URI(LILV_NS_XSD "double"); | |||
world->uris.xsd_integer = NEW_URI(LILV_NS_XSD "integer"); | |||
world->uris.null_uri = NULL; | |||
world->lv2_plugin_class = lilv_plugin_class_new( | |||
world, NULL, world->uris.lv2_Plugin, "Plugin"); | |||
assert(world->lv2_plugin_class); | |||
world->n_read_files = 0; | |||
world->opt.filter_language = true; | |||
world->opt.dyn_manifest = true; | |||
return world; | |||
fail: | |||
/* keep on rockin' in the */ free(world); | |||
return NULL; | |||
} | |||
LILV_API | |||
void | |||
lilv_world_free(LilvWorld* world) | |||
{ | |||
if (!world) { | |||
return; | |||
} | |||
lilv_plugin_class_free(world->lv2_plugin_class); | |||
world->lv2_plugin_class = NULL; | |||
for (SordNode** n = (SordNode**)&world->uris; *n; ++n) { | |||
sord_node_free(world->world, *n); | |||
} | |||
for (LilvSpec* spec = world->specs; spec;) { | |||
LilvSpec* next = spec->next; | |||
sord_node_free(world->world, spec->spec); | |||
sord_node_free(world->world, spec->bundle); | |||
lilv_nodes_free(spec->data_uris); | |||
free(spec); | |||
spec = next; | |||
} | |||
world->specs = NULL; | |||
LILV_FOREACH(plugins, i, world->plugins) { | |||
const LilvPlugin* p = lilv_plugins_get(world->plugins, i); | |||
lilv_plugin_free((LilvPlugin*)p); | |||
} | |||
zix_tree_free((ZixTree*)world->plugins); | |||
world->plugins = NULL; | |||
zix_tree_free((ZixTree*)world->loaded_files); | |||
world->loaded_files = NULL; | |||
#ifdef LILV_NEW_LV2 | |||
zix_tree_free((ZixTree*)world->libs); | |||
world->libs = NULL; | |||
#endif | |||
zix_tree_free((ZixTree*)world->plugin_classes); | |||
world->plugin_classes = NULL; | |||
sord_free(world->model); | |||
world->model = NULL; | |||
sord_world_free(world->world); | |||
world->world = NULL; | |||
free(world); | |||
} | |||
LILV_API | |||
void | |||
lilv_world_set_option(LilvWorld* world, | |||
const char* option, | |||
const LilvNode* value) | |||
{ | |||
if (!strcmp(option, LILV_OPTION_DYN_MANIFEST)) { | |||
if (lilv_node_is_bool(value)) { | |||
world->opt.dyn_manifest = lilv_node_as_bool(value); | |||
return; | |||
} | |||
} else if (!strcmp(option, LILV_OPTION_FILTER_LANG)) { | |||
if (lilv_node_is_bool(value)) { | |||
world->opt.filter_language = lilv_node_as_bool(value); | |||
return; | |||
} | |||
} | |||
LILV_WARNF("Unrecognized or invalid option `%s'\n", option); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_world_find_nodes(LilvWorld* world, | |||
const LilvNode* subject, | |||
const LilvNode* predicate, | |||
const LilvNode* object) | |||
{ | |||
if (subject && !lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) { | |||
LILV_ERRORF("Subject `%s' is not a resource\n", | |||
sord_node_get_string(subject->node)); | |||
return NULL; | |||
} | |||
if (!lilv_node_is_uri(predicate)) { | |||
LILV_ERRORF("Predicate `%s' is not a URI\n", | |||
sord_node_get_string(predicate->node)); | |||
return NULL; | |||
} | |||
if (!subject && !object) { | |||
LILV_ERROR("Both subject and object are NULL\n"); | |||
return NULL; | |||
} | |||
SordNode* const subject_node = subject | |||
? sord_node_copy(subject->node) | |||
: NULL; | |||
SordNode* const object_node = object | |||
? sord_node_copy(object->node) | |||
: NULL; | |||
LilvNodes* ret = lilv_world_query_values_internal( | |||
world, subject_node, predicate->node, object_node); | |||
sord_node_free(world->world, subject_node); | |||
sord_node_free(world->world, object_node); | |||
return ret; | |||
} | |||
SordIter* | |||
lilv_world_query_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object) | |||
{ | |||
return sord_search(world->model, subject, predicate, object, NULL); | |||
} | |||
LilvNodes* | |||
lilv_world_query_values_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object) | |||
{ | |||
return lilv_nodes_from_stream_objects( | |||
world, | |||
lilv_world_query_internal(world, subject, predicate, object), | |||
(object == NULL) ? SORD_OBJECT : SORD_SUBJECT); | |||
} | |||
static SerdNode | |||
lilv_new_uri_relative_to_base(const uint8_t* uri_str, | |||
const uint8_t* base_uri_str) | |||
{ | |||
SerdURI base_uri; | |||
if (serd_uri_parse(base_uri_str, &base_uri)) { | |||
return SERD_NODE_NULL; | |||
} | |||
SerdURI ignored; | |||
return serd_node_new_uri_from_string(uri_str, &base_uri, &ignored); | |||
} | |||
const uint8_t* | |||
lilv_world_blank_node_prefix(LilvWorld* world) | |||
{ | |||
static char str[32]; | |||
snprintf(str, sizeof(str), "%d", world->n_read_files++); | |||
return (const uint8_t*)str; | |||
} | |||
/** Comparator for sequences (e.g. world->plugins). */ | |||
int | |||
lilv_header_compare_by_uri(const void* a, const void* b, void* user_data) | |||
{ | |||
const struct LilvHeader* const header_a = (const struct LilvHeader*)a; | |||
const struct LilvHeader* const header_b = (const struct LilvHeader*)b; | |||
return strcmp(lilv_node_as_uri(header_a->uri), | |||
lilv_node_as_uri(header_b->uri)); | |||
} | |||
/** Get an element of a collection of any object with an LilvHeader by URI. */ | |||
struct LilvHeader* | |||
lilv_collection_get_by_uri(const ZixTree* const_seq, | |||
const LilvNode* uri) | |||
{ | |||
if (!lilv_node_is_uri(uri)) { | |||
return NULL; | |||
} | |||
ZixTree* seq = (ZixTree*)const_seq; | |||
struct LilvHeader key = { NULL, (LilvNode*)uri }; | |||
ZixTreeIter* i = NULL; | |||
ZixStatus st = zix_tree_find(seq, &key, &i); | |||
if (!st) { | |||
return (struct LilvHeader*)zix_tree_get(i); | |||
} | |||
return NULL; | |||
} | |||
static void | |||
lilv_world_add_spec(LilvWorld* world, | |||
const SordNode* specification_node, | |||
const SordNode* bundle_node) | |||
{ | |||
LilvSpec* spec = (LilvSpec*)malloc(sizeof(LilvSpec)); | |||
spec->spec = sord_node_copy(specification_node); | |||
spec->bundle = sord_node_copy(bundle_node); | |||
spec->data_uris = lilv_nodes_new(); | |||
// Add all plugin data files (rdfs:seeAlso) | |||
SordIter* files = sord_search( | |||
world->model, | |||
specification_node, | |||
world->uris.rdfs_seeAlso, | |||
NULL, | |||
NULL); | |||
FOREACH_MATCH(files) { | |||
const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT); | |||
zix_tree_insert((ZixTree*)spec->data_uris, | |||
lilv_node_new_from_node(world, file_node), | |||
NULL); | |||
} | |||
sord_iter_free(files); | |||
// Add specification to world specification list | |||
spec->next = world->specs; | |||
world->specs = spec; | |||
} | |||
static void | |||
lilv_world_add_plugin(LilvWorld* world, | |||
const SordNode* plugin_node, | |||
SerdNode* manifest_uri, | |||
void* dynmanifest, | |||
const SordNode* bundle_node) | |||
{ | |||
LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node); | |||
const LilvPlugin* last = lilv_plugins_get_by_uri(world->plugins, | |||
plugin_uri); | |||
if (last) { | |||
LILV_ERRORF("Duplicate plugin <%s>\n", lilv_node_as_uri(plugin_uri)); | |||
LILV_ERRORF("... found in %s\n", lilv_node_as_string( | |||
lilv_plugin_get_bundle_uri(last))); | |||
LILV_ERRORF("... and %s\n", sord_node_get_string(bundle_node)); | |||
lilv_node_free(plugin_uri); | |||
return; | |||
} | |||
// Create LilvPlugin | |||
LilvNode* bundle_uri = lilv_node_new_from_node(world, bundle_node); | |||
LilvPlugin* plugin = lilv_plugin_new(world, plugin_uri, bundle_uri); | |||
// Add manifest as plugin data file (as if it were rdfs:seeAlso) | |||
zix_tree_insert((ZixTree*)plugin->data_uris, | |||
lilv_new_uri(world, (const char*)manifest_uri->buf), | |||
NULL); | |||
#ifdef LILV_DYN_MANIFEST | |||
// Set dynamic manifest library URI, if applicable | |||
if (dynmanifest) { | |||
plugin->dynmanifest = (LilvDynManifest*)dynmanifest; | |||
++((LilvDynManifest*)dynmanifest)->refs; | |||
} | |||
#endif | |||
// Add all plugin data files (rdfs:seeAlso) | |||
SordIter* files = sord_search( | |||
world->model, | |||
plugin_node, | |||
world->uris.rdfs_seeAlso, | |||
NULL, | |||
NULL); | |||
FOREACH_MATCH(files) { | |||
const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT); | |||
zix_tree_insert((ZixTree*)plugin->data_uris, | |||
lilv_node_new_from_node(world, file_node), | |||
NULL); | |||
} | |||
sord_iter_free(files); | |||
// Add plugin to world plugin sequence | |||
zix_tree_insert((ZixTree*)world->plugins, plugin, NULL); | |||
} | |||
static void | |||
lilv_world_load_dyn_manifest(LilvWorld* world, | |||
SordNode* bundle_node, | |||
SerdNode manifest_uri) | |||
{ | |||
#ifdef LILV_DYN_MANIFEST | |||
if (!world->opt.dyn_manifest) { | |||
return; | |||
} | |||
typedef void* LV2_Dyn_Manifest_Handle; | |||
LV2_Dyn_Manifest_Handle handle = NULL; | |||
// ?dman a dynman:DynManifest | |||
SordIter* dmanifests = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.dman_DynManifest, | |||
bundle_node); | |||
FOREACH_MATCH(dmanifests) { | |||
const SordNode* dmanifest = sord_iter_get_node(dmanifests, SORD_SUBJECT); | |||
// ?dman lv2:binary ?binary | |||
SordIter* binaries = sord_search( | |||
world->model, | |||
dmanifest, | |||
world->uris.lv2_binary, | |||
NULL, | |||
bundle_node); | |||
if (sord_iter_end(binaries)) { | |||
sord_iter_free(binaries); | |||
LILV_ERRORF("Dynamic manifest in <%s> has no binaries, ignored\n", | |||
sord_node_get_string(bundle_node)); | |||
continue; | |||
} | |||
// Get binary path | |||
const SordNode* binary = sord_iter_get_node(binaries, SORD_OBJECT); | |||
const uint8_t* lib_uri = sord_node_get_string(binary); | |||
const char* lib_path = lilv_uri_to_path((const char*)lib_uri); | |||
if (!lib_path) { | |||
LILV_ERROR("No dynamic manifest library path\n"); | |||
sord_iter_free(binaries); | |||
continue; | |||
} | |||
// Open library | |||
void* lib = dlopen(lib_path, RTLD_LAZY); | |||
if (!lib) { | |||
LILV_ERRORF("Failed to open dynmanifest library `%s'\n", lib_path); | |||
sord_iter_free(binaries); | |||
continue; | |||
} | |||
// Open dynamic manifest | |||
typedef int (*OpenFunc)(LV2_Dyn_Manifest_Handle*, | |||
const LV2_Feature *const *); | |||
OpenFunc dmopen = (OpenFunc)lilv_dlfunc(lib, "lv2_dyn_manifest_open"); | |||
if (!dmopen || dmopen(&handle, &dman_features)) { | |||
LILV_ERRORF("No `lv2_dyn_manifest_open' in `%s'\n", lib_path); | |||
sord_iter_free(binaries); | |||
dlclose(lib); | |||
continue; | |||
} | |||
// Get subjects (the data that would be in manifest.ttl) | |||
typedef int (*GetSubjectsFunc)(LV2_Dyn_Manifest_Handle, FILE*); | |||
GetSubjectsFunc get_subjects_func = (GetSubjectsFunc)lilv_dlfunc( | |||
lib, "lv2_dyn_manifest_get_subjects"); | |||
if (!get_subjects_func) { | |||
LILV_ERRORF("No `lv2_dyn_manifest_get_subjects' in `%s'\n", | |||
lib_path); | |||
sord_iter_free(binaries); | |||
dlclose(lib); | |||
continue; | |||
} | |||
LilvDynManifest* desc = malloc(sizeof(LilvDynManifest)); | |||
desc->bundle = lilv_node_new_from_node(world, bundle_node); | |||
desc->lib = lib; | |||
desc->handle = handle; | |||
desc->refs = 0; | |||
// Generate data file | |||
FILE* fd = tmpfile(); | |||
get_subjects_func(handle, fd); | |||
rewind(fd); | |||
// Parse generated data file | |||
const SerdNode* base = sord_node_to_serd_node(dmanifest); | |||
SerdEnv* env = serd_env_new(base); | |||
SerdReader* reader = sord_new_reader( | |||
world->model, env, SERD_TURTLE, sord_node_copy(dmanifest)); | |||
serd_reader_add_blank_prefix(reader, | |||
lilv_world_blank_node_prefix(world)); | |||
serd_reader_read_file_handle(reader, fd, | |||
(const uint8_t*)"(dyn-manifest)"); | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
// Close (and automatically delete) temporary data file | |||
fclose(fd); | |||
// ?plugin a lv2:Plugin | |||
SordIter* plug_results = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.lv2_Plugin, | |||
dmanifest); | |||
FOREACH_MATCH(plug_results) { | |||
const SordNode* plugin_node = sord_iter_get_node(plug_results, SORD_SUBJECT); | |||
lilv_world_add_plugin(world, plugin_node, | |||
&manifest_uri, desc, bundle_node); | |||
} | |||
sord_iter_free(plug_results); | |||
sord_iter_free(binaries); | |||
} | |||
sord_iter_free(dmanifests); | |||
#endif // LILV_DYN_MANIFEST | |||
} | |||
LILV_API | |||
void | |||
lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) | |||
{ | |||
if (!lilv_node_is_uri(bundle_uri)) { | |||
LILV_ERRORF("Bundle URI `%s' is not a URI\n", | |||
sord_node_get_string(bundle_uri->node)); | |||
return; | |||
} | |||
SordNode* bundle_node = bundle_uri->node; | |||
SerdNode manifest_uri = lilv_new_uri_relative_to_base( | |||
(const uint8_t*)"manifest.ttl", | |||
(const uint8_t*)sord_node_get_string(bundle_node)); | |||
SerdEnv* env = serd_env_new(&manifest_uri); | |||
SerdReader* reader = sord_new_reader(world->model, env, SERD_TURTLE, | |||
bundle_node); | |||
serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world)); | |||
SerdStatus st = serd_reader_read_file(reader, manifest_uri.buf); | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
if (st) { | |||
LILV_ERRORF("Error reading %s\n", manifest_uri.buf); | |||
return; | |||
} | |||
// ?plugin a lv2:Plugin | |||
SordIter* plug_results = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.lv2_Plugin, | |||
bundle_node); | |||
FOREACH_MATCH(plug_results) { | |||
const SordNode* plugin_node = sord_iter_get_node(plug_results, SORD_SUBJECT); | |||
lilv_world_add_plugin(world, plugin_node, | |||
&manifest_uri, NULL, bundle_node); | |||
} | |||
sord_iter_free(plug_results); | |||
lilv_world_load_dyn_manifest(world, bundle_node, manifest_uri); | |||
// ?specification a lv2:Specification | |||
SordIter* spec_results = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.lv2_Specification, | |||
bundle_node); | |||
FOREACH_MATCH(spec_results) { | |||
const SordNode* spec = sord_iter_get_node(spec_results, SORD_SUBJECT); | |||
lilv_world_add_spec(world, spec, bundle_node); | |||
} | |||
sord_iter_free(spec_results); | |||
serd_node_free(&manifest_uri); | |||
} | |||
static void | |||
load_dir_entry(const char* dir, const char* name, void* data) | |||
{ | |||
LilvWorld* world = (LilvWorld*)data; | |||
if (!strcmp(name, ".") || !strcmp(name, "..")) | |||
return; | |||
const char* scheme = (dir[0] == '/') ? "file://" : "file:///"; | |||
char* uri = lilv_strjoin(scheme, dir, "/", name, "/", NULL); | |||
LilvNode* uri_val = lilv_new_uri(world, uri); | |||
lilv_world_load_bundle(world, uri_val); | |||
lilv_node_free(uri_val); | |||
free(uri); | |||
} | |||
/** Load all bundles in the directory at @a dir_path. */ | |||
static void | |||
lilv_world_load_directory(LilvWorld* world, const char* dir_path) | |||
{ | |||
char* path = lilv_expand(dir_path); | |||
if (!path) { | |||
LILV_WARNF("Empty path `%s'\n", path); | |||
return; | |||
} | |||
lilv_dir_for_each(path, world, load_dir_entry); | |||
free(path); | |||
} | |||
static bool | |||
is_path_sep(char c) | |||
{ | |||
return c == LILV_PATH_SEP[0]; | |||
} | |||
static const char* | |||
first_path_sep(const char* path) | |||
{ | |||
for (const char* p = path; *p != '\0'; ++p) { | |||
if (is_path_sep(*p)) { | |||
return p; | |||
} | |||
} | |||
return NULL; | |||
} | |||
/** Load all bundles found in @a lv2_path. | |||
* @param lv2_path A colon-delimited list of directories. These directories | |||
* should contain LV2 bundle directories (ie the search path is a list of | |||
* parent directories of bundles, not a list of bundle directories). | |||
*/ | |||
static void | |||
lilv_world_load_path(LilvWorld* world, | |||
const char* lv2_path) | |||
{ | |||
while (lv2_path[0] != '\0') { | |||
const char* const sep = first_path_sep(lv2_path); | |||
if (sep) { | |||
const size_t dir_len = sep - lv2_path; | |||
char* const dir = (char*)malloc(dir_len + 1); | |||
memcpy(dir, lv2_path, dir_len); | |||
dir[dir_len] = '\0'; | |||
lilv_world_load_directory(world, dir); | |||
free(dir); | |||
lv2_path += dir_len + 1; | |||
} else { | |||
lilv_world_load_directory(world, lv2_path); | |||
lv2_path = "\0"; | |||
} | |||
} | |||
} | |||
static void | |||
lilv_world_load_specifications(LilvWorld* world) | |||
{ | |||
for (LilvSpec* spec = world->specs; spec; spec = spec->next) { | |||
LILV_FOREACH(nodes, f, spec->data_uris) { | |||
LilvNode* file = (LilvNode*)lilv_collection_get(spec->data_uris, f); | |||
const SerdNode* node = sord_node_to_serd_node(file->node); | |||
SerdEnv* env = serd_env_new(node); | |||
SerdReader* reader = sord_new_reader(world->model, env, | |||
SERD_TURTLE, NULL); | |||
serd_reader_add_blank_prefix(reader, | |||
lilv_world_blank_node_prefix(world)); | |||
serd_reader_read_file(reader, node->buf); | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
} | |||
} | |||
} | |||
static void | |||
lilv_world_load_plugin_classes(LilvWorld* world) | |||
{ | |||
/* FIXME: This loads all classes, not just lv2:Plugin subclasses. | |||
However, if the host gets all the classes via lilv_plugin_class_get_children | |||
starting with lv2:Plugin as the root (which is e.g. how a host would build | |||
a menu), they won't be seen anyway... | |||
*/ | |||
SordIter* classes = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.rdfs_Class, | |||
NULL); | |||
FOREACH_MATCH(classes) { | |||
const SordNode* class_node = sord_iter_get_node(classes, SORD_SUBJECT); | |||
// Get parents (superclasses) | |||
SordIter* parents = sord_search( | |||
world->model, | |||
class_node, | |||
world->uris.rdfs_subClassOf, | |||
NULL, | |||
NULL); | |||
if (sord_iter_end(parents)) { | |||
sord_iter_free(parents); | |||
continue; | |||
} | |||
const SordNode* parent_node = sord_iter_get_node(parents, SORD_OBJECT); | |||
sord_iter_free(parents); | |||
if (!sord_node_get_type(parent_node) == SORD_URI) { | |||
// Class parent is not a resource, ignore (e.g. owl restriction) | |||
continue; | |||
} | |||
// Get labels | |||
SordIter* labels = sord_search( | |||
world->model, | |||
class_node, | |||
world->uris.rdfs_label, | |||
NULL, | |||
NULL); | |||
if (sord_iter_end(labels)) { | |||
sord_iter_free(labels); | |||
continue; | |||
} | |||
const SordNode* label_node = sord_iter_get_node(labels, SORD_OBJECT); | |||
const uint8_t* label = sord_node_get_string(label_node); | |||
sord_iter_free(labels); | |||
LilvPluginClass* pclass = lilv_plugin_class_new( | |||
world, parent_node, class_node, (const char*)label); | |||
if (pclass) { | |||
zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL); | |||
} | |||
} | |||
sord_iter_free(classes); | |||
} | |||
LILV_API | |||
void | |||
lilv_world_load_all(LilvWorld* world) | |||
{ | |||
const char* lv2_path = getenv("LV2_PATH"); | |||
if (!lv2_path) | |||
lv2_path = LILV_DEFAULT_LV2_PATH; | |||
// Discover bundles and read all manifest files into model | |||
lilv_world_load_path(world, lv2_path); | |||
LILV_FOREACH(plugins, p, world->plugins) { | |||
const LilvPlugin* plugin = (const LilvPlugin*)lilv_collection_get( | |||
(ZixTree*)world->plugins, p); | |||
// ?new dc:replaces plugin | |||
SordIter* replacement = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.dc_replaces, | |||
lilv_node_as_node(lilv_plugin_get_uri(plugin)), | |||
NULL); | |||
if (!sord_iter_end(replacement)) { | |||
/* TODO: Check if replacement is actually a known plugin, | |||
though this is expensive... | |||
*/ | |||
((LilvPlugin*)plugin)->replaced = true; | |||
} | |||
sord_iter_free(replacement); | |||
} | |||
// Query out things to cache | |||
lilv_world_load_specifications(world); | |||
lilv_world_load_plugin_classes(world); | |||
} | |||
LILV_API | |||
int | |||
lilv_world_load_resource(LilvWorld* world, | |||
const LilvNode* resource) | |||
{ | |||
if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) { | |||
LILV_ERRORF("Node `%s' is not a resource\n", | |||
sord_node_get_string(resource->node)); | |||
return -1; | |||
} | |||
int n_read = 0; | |||
SordIter* files = sord_search(world->model, | |||
resource->node, | |||
world->uris.rdfs_seeAlso, | |||
NULL, NULL); | |||
FOREACH_MATCH(files) { | |||
const SordNode* file = sord_iter_get_node(files, SORD_OBJECT); | |||
const uint8_t* str = sord_node_get_string(file); | |||
LilvNode* file_node = lilv_node_new_from_node(world, file); | |||
ZixTreeIter* iter; | |||
if (zix_tree_find((ZixTree*)world->loaded_files, file_node, &iter)) { | |||
if (sord_node_get_type(file) == SORD_URI) { | |||
const SerdNode* base = sord_node_to_serd_node(file); | |||
SerdEnv* env = serd_env_new(base); | |||
SerdReader* reader = sord_new_reader( | |||
world->model, env, SERD_TURTLE, (SordNode*)file); | |||
serd_reader_add_blank_prefix( | |||
reader, lilv_world_blank_node_prefix(world)); | |||
if (!serd_reader_read_file(reader, str)) { | |||
++n_read; | |||
zix_tree_insert( | |||
(ZixTree*)world->loaded_files, file_node, NULL); | |||
file_node = NULL; // prevent deletion... | |||
} else { | |||
LILV_ERRORF("Error loading resource `%s'\n", str); | |||
} | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
} else { | |||
LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", str); | |||
} | |||
} | |||
lilv_node_free(file_node); // ...here | |||
} | |||
sord_iter_free(files); | |||
return n_read; | |||
} | |||
LILV_API | |||
const LilvPluginClass* | |||
lilv_world_get_plugin_class(const LilvWorld* world) | |||
{ | |||
return world->lv2_plugin_class; | |||
} | |||
LILV_API | |||
const LilvPluginClasses* | |||
lilv_world_get_plugin_classes(const LilvWorld* world) | |||
{ | |||
return world->plugin_classes; | |||
} | |||
LILV_API | |||
const LilvPlugins* | |||
lilv_world_get_all_plugins(const LilvWorld* world) | |||
{ | |||
return world->plugins; | |||
} |
@@ -0,0 +1,88 @@ | |||
/* | |||
Copyright 2011 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 ZIX_COMMON_H | |||
#define ZIX_COMMON_H | |||
/** | |||
@addtogroup zix | |||
@{ | |||
*/ | |||
/** @cond */ | |||
#ifdef ZIX_SHARED | |||
# ifdef _WIN32 | |||
# define ZIX_LIB_IMPORT __declspec(dllimport) | |||
# define ZIX_LIB_EXPORT __declspec(dllexport) | |||
# else | |||
# define ZIX_LIB_IMPORT __attribute__((visibility("default"))) | |||
# define ZIX_LIB_EXPORT __attribute__((visibility("default"))) | |||
# endif | |||
# ifdef ZIX_INTERNAL | |||
# define ZIX_API ZIX_LIB_EXPORT | |||
# else | |||
# define ZIX_API ZIX_LIB_IMPORT | |||
# endif | |||
# define ZIX_PRIVATE static | |||
#elif defined(ZIX_INLINE) | |||
# define ZIX_API static inline | |||
# define ZIX_PRIVATE static inline | |||
#else | |||
# define ZIX_API | |||
# define ZIX_PRIVATE static | |||
#endif | |||
/** @endcond */ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
typedef enum { | |||
ZIX_STATUS_SUCCESS, | |||
ZIX_STATUS_ERROR, | |||
ZIX_STATUS_NO_MEM, | |||
ZIX_STATUS_NOT_FOUND, | |||
ZIX_STATUS_EXISTS, | |||
ZIX_STATUS_BAD_ARG, | |||
ZIX_STATUS_BAD_PERMS, | |||
} ZixStatus; | |||
/** | |||
Function for comparing two elements. | |||
*/ | |||
typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); | |||
/** | |||
Function for testing equality of two elements. | |||
*/ | |||
typedef bool (*ZixEqualFunc)(const void* a, const void* b); | |||
/** | |||
Function to destroy an element. | |||
*/ | |||
typedef void (*ZixDestroyFunc)(void* ptr); | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* ZIX_COMMON_H */ |
@@ -0,0 +1,716 @@ | |||
/* | |||
Copyright 2011 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 <assert.h> | |||
#include <stdint.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "zix/common.h" | |||
#include "zix/tree.h" | |||
typedef struct ZixTreeNodeImpl ZixTreeNode; | |||
struct ZixTreeImpl { | |||
ZixTreeNode* root; | |||
ZixDestroyFunc destroy; | |||
ZixComparator cmp; | |||
void* cmp_data; | |||
size_t size; | |||
bool allow_duplicates; | |||
}; | |||
struct ZixTreeNodeImpl { | |||
void* data; | |||
struct ZixTreeNodeImpl* left; | |||
struct ZixTreeNodeImpl* right; | |||
struct ZixTreeNodeImpl* parent; | |||
int_fast8_t balance; | |||
}; | |||
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |||
// Uncomment these for debugging features | |||
// #define ZIX_TREE_DUMP 1 | |||
// #define ZIX_TREE_VERIFY 1 | |||
// #define ZIX_TREE_HYPER_VERIFY 1 | |||
#if defined(ZIX_TREE_VERIFY) || defined(ZIX_TREE_HYPER_VERIFY) | |||
# include "tree_debug.h" | |||
# define ASSERT_BALANCE(n) assert(verify_balance(n)) | |||
#else | |||
# define ASSERT_BALANCE(n) | |||
#endif | |||
#ifdef ZIX_TREE_DUMP | |||
# include "tree_debug.h" | |||
# define DUMP(t) zix_tree_print(t->root, 0) | |||
# define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) | |||
#else | |||
# define DUMP(t) | |||
# define DEBUG_PRINTF(fmt, ...) | |||
#endif | |||
ZIX_API ZixTree* | |||
zix_tree_new(bool allow_duplicates, | |||
ZixComparator cmp, | |||
void* cmp_data, | |||
ZixDestroyFunc destroy) | |||
{ | |||
ZixTree* t = (ZixTree*)malloc(sizeof(ZixTree)); | |||
t->root = NULL; | |||
t->destroy = destroy; | |||
t->cmp = cmp; | |||
t->cmp_data = cmp_data; | |||
t->size = 0; | |||
t->allow_duplicates = allow_duplicates; | |||
return t; | |||
} | |||
ZIX_PRIVATE void | |||
zix_tree_free_rec(ZixTree* t, ZixTreeNode* n) | |||
{ | |||
if (n) { | |||
zix_tree_free_rec(t, n->left); | |||
zix_tree_free_rec(t, n->right); | |||
if (t->destroy) { | |||
t->destroy(n->data); | |||
} | |||
free(n); | |||
} | |||
} | |||
ZIX_API void | |||
zix_tree_free(ZixTree* t) | |||
{ | |||
if (t) { | |||
zix_tree_free_rec(t, t->root); | |||
free(t); | |||
} | |||
} | |||
ZIX_API size_t | |||
zix_tree_size(const ZixTree* t) | |||
{ | |||
return t->size; | |||
} | |||
ZIX_PRIVATE void | |||
rotate(ZixTreeNode* p, ZixTreeNode* q) | |||
{ | |||
assert(q->parent == p); | |||
assert(p->left == q || p->right == q); | |||
q->parent = p->parent; | |||
if (q->parent) { | |||
if (q->parent->left == p) { | |||
q->parent->left = q; | |||
} else { | |||
q->parent->right = q; | |||
} | |||
} | |||
if (p->right == q) { | |||
// Rotate left | |||
p->right = q->left; | |||
q->left = p; | |||
if (p->right) { | |||
p->right->parent = p; | |||
} | |||
} else { | |||
// Rotate right | |||
assert(p->left == q); | |||
p->left = q->right; | |||
q->right = p; | |||
if (p->left) { | |||
p->left->parent = p; | |||
} | |||
} | |||
p->parent = q; | |||
} | |||
/** | |||
* Rotate left about @a p. | |||
* | |||
* p q | |||
* / \ / \ | |||
* A q => p C | |||
* / \ / \ | |||
* B C A B | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_left(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->right; | |||
*height_change = (q->balance == 0) ? 0 : -1; | |||
DEBUG_PRINTF("LL %ld\n", (intptr_t)p->data); | |||
assert(p->balance == 2); | |||
assert(q->balance == 0 || q->balance == 1); | |||
rotate(p, q); | |||
// p->balance -= 1 + MAX(0, q->balance); | |||
// q->balance -= 1 - MIN(0, p->balance); | |||
--q->balance; | |||
p->balance = -(q->balance); | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
return q; | |||
} | |||
/** | |||
* Rotate right about @a p. | |||
* | |||
* p q | |||
* / \ / \ | |||
* q C => A p | |||
* / \ / \ | |||
* A B B C | |||
* | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_right(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->left; | |||
*height_change = (q->balance == 0) ? 0 : -1; | |||
DEBUG_PRINTF("RR %ld\n", (intptr_t)p->data); | |||
assert(p->balance == -2); | |||
assert(q->balance == 0 || q->balance == -1); | |||
rotate(p, q); | |||
// p->balance += 1 - MIN(0, q->balance); | |||
// q->balance += 1 + MAX(0, p->balance); | |||
++q->balance; | |||
p->balance = -(q->balance); | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
return q; | |||
} | |||
/** | |||
* Rotate left about @a p->left then right about @a p. | |||
* | |||
* p r | |||
* / \ / \ | |||
* q D => q p | |||
* / \ / \ / \ | |||
* A r A B C D | |||
* / \ | |||
* B C | |||
* | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_left_right(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->left; | |||
ZixTreeNode* const r = q->right; | |||
assert(p->balance == -2); | |||
assert(q->balance == 1); | |||
assert(r->balance == -1 || r->balance == 0 || r->balance == 1); | |||
DEBUG_PRINTF("LR %ld P: %2d Q: %2d R: %2d\n", | |||
(intptr_t)p->data, p->balance, q->balance, r->balance); | |||
rotate(q, r); | |||
rotate(p, r); | |||
q->balance -= 1 + MAX(0, r->balance); | |||
p->balance += 1 - MIN(MIN(0, r->balance) - 1, r->balance + q->balance); | |||
// r->balance += MAX(0, p->balance) + MIN(0, q->balance); | |||
// p->balance = (p->left && p->right) ? -MIN(r->balance, 0) : 0; | |||
// q->balance = - MAX(r->balance, 0); | |||
r->balance = 0; | |||
*height_change = -1; | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
ASSERT_BALANCE(r); | |||
return r; | |||
} | |||
/** | |||
* Rotate right about @a p->right then right about @a p. | |||
* | |||
* p r | |||
* / \ / \ | |||
* A q => p q | |||
* / \ / \ / \ | |||
* r D A B C D | |||
* / \ | |||
* B C | |||
* | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_right_left(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->right; | |||
ZixTreeNode* const r = q->left; | |||
assert(p->balance == 2); | |||
assert(q->balance == -1); | |||
assert(r->balance == -1 || r->balance == 0 || r->balance == 1); | |||
DEBUG_PRINTF("RL %ld P: %2d Q: %2d R: %2d\n", | |||
(intptr_t)p->data, p->balance, q->balance, r->balance); | |||
rotate(q, r); | |||
rotate(p, r); | |||
q->balance += 1 - MIN(0, r->balance); | |||
p->balance -= 1 + MAX(MAX(0, r->balance) + 1, r->balance + q->balance); | |||
// r->balance += MAX(0, q->balance) + MIN(0, p->balance); | |||
// p->balance = (p->left && p->right) ? -MAX(r->balance, 0) : 0; | |||
// q->balance = - MIN(r->balance, 0); | |||
r->balance = 0; | |||
// assert(r->balance == 0); | |||
*height_change = -1; | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
ASSERT_BALANCE(r); | |||
return r; | |||
} | |||
ZIX_PRIVATE ZixTreeNode* | |||
zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) | |||
{ | |||
#ifdef ZIX_TREE_HYPER_VERIFY | |||
const size_t old_height = height(node); | |||
#endif | |||
DEBUG_PRINTF("REBALANCE %ld (%d)\n", (intptr_t)node->data, node->balance); | |||
*height_change = 0; | |||
const bool is_root = !node->parent; | |||
assert((is_root && t->root == node) || (!is_root && t->root != node)); | |||
ZixTreeNode* replacement = node; | |||
if (node->balance == -2) { | |||
assert(node->left); | |||
if (node->left->balance == 1) { | |||
replacement = rotate_left_right(node, height_change); | |||
} else { | |||
replacement = rotate_right(node, height_change); | |||
} | |||
} else if (node->balance == 2) { | |||
assert(node->right); | |||
if (node->right->balance == -1) { | |||
replacement = rotate_right_left(node, height_change); | |||
} else { | |||
replacement = rotate_left(node, height_change); | |||
} | |||
} | |||
if (is_root) { | |||
assert(!replacement->parent); | |||
t->root = replacement; | |||
} | |||
DUMP(t); | |||
#ifdef ZIX_TREE_HYPER_VERIFY | |||
assert(old_height + *height_change == height(replacement)); | |||
#endif | |||
return replacement; | |||
} | |||
ZIX_API ZixStatus | |||
zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti) | |||
{ | |||
DEBUG_PRINTF("**** INSERT %ld\n", (intptr_t)e); | |||
int cmp = 0; | |||
ZixTreeNode* n = t->root; | |||
ZixTreeNode* p = NULL; | |||
// Find the parent p of e | |||
while (n) { | |||
p = n; | |||
cmp = t->cmp(e, n->data, t->cmp_data); | |||
if (cmp < 0) { | |||
n = n->left; | |||
} else if (cmp > 0) { | |||
n = n->right; | |||
} else if (t->allow_duplicates) { | |||
n = n->right; | |||
} else { | |||
if (ti) { | |||
*ti = n; | |||
} | |||
DEBUG_PRINTF("%ld EXISTS!\n", (intptr_t)e); | |||
return ZIX_STATUS_EXISTS; | |||
} | |||
} | |||
// Allocate a new node n | |||
if (!(n = (ZixTreeNode*)malloc(sizeof(ZixTreeNode)))) { | |||
return ZIX_STATUS_NO_MEM; | |||
} | |||
memset(n, '\0', sizeof(ZixTreeNode)); | |||
n->data = e; | |||
n->balance = 0; | |||
if (ti) { | |||
*ti = n; | |||
} | |||
bool p_height_increased = false; | |||
// Make p the parent of n | |||
n->parent = p; | |||
if (!p) { | |||
t->root = n; | |||
} else { | |||
if (cmp < 0) { | |||
assert(!p->left); | |||
assert(p->balance == 0 || p->balance == 1); | |||
p->left = n; | |||
--p->balance; | |||
p_height_increased = !p->right; | |||
} else { | |||
assert(!p->right); | |||
assert(p->balance == 0 || p->balance == -1); | |||
p->right = n; | |||
++p->balance; | |||
p_height_increased = !p->left; | |||
} | |||
} | |||
DUMP(t); | |||
// Rebalance if necessary (at most 1 rotation) | |||
assert(!p || p->balance == -1 || p->balance == 0 || p->balance == 1); | |||
if (p && p_height_increased) { | |||
int height_change = 0; | |||
for (ZixTreeNode* i = p; i && i->parent; i = i->parent) { | |||
if (i == i->parent->left) { | |||
if (--i->parent->balance == -2) { | |||
zix_tree_rebalance(t, i->parent, &height_change); | |||
break; | |||
} | |||
} else { | |||
assert(i == i->parent->right); | |||
if (++i->parent->balance == 2) { | |||
zix_tree_rebalance(t, i->parent, &height_change); | |||
break; | |||
} | |||
} | |||
if (i->parent->balance == 0) { | |||
break; | |||
} | |||
} | |||
} | |||
DUMP(t); | |||
++t->size; | |||
#ifdef ZIX_TREE_VERIFY | |||
if (!verify(t, t->root)) { | |||
return ZIX_STATUS_ERROR; | |||
} | |||
#endif | |||
return ZIX_STATUS_SUCCESS; | |||
} | |||
ZIX_API ZixStatus | |||
zix_tree_remove(ZixTree* t, ZixTreeIter* ti) | |||
{ | |||
ZixTreeNode* const n = ti; | |||
ZixTreeNode** pp = NULL; // parent pointer | |||
ZixTreeNode* to_balance = n->parent; // lowest node to balance | |||
int8_t d_balance = 0; // delta(balance) for n->parent | |||
DEBUG_PRINTF("*** REMOVE %ld\n", (intptr_t)n->data); | |||
if ((n == t->root) && !n->left && !n->right) { | |||
t->root = NULL; | |||
if (t->destroy) { | |||
t->destroy(n->data); | |||
} | |||
free(n); | |||
--t->size; | |||
assert(t->size == 0); | |||
return ZIX_STATUS_SUCCESS; | |||
} | |||
// Set pp to the parent pointer to n, if applicable | |||
if (n->parent) { | |||
assert(n->parent->left == n || n->parent->right == n); | |||
if (n->parent->left == n) { // n is left child | |||
pp = &n->parent->left; | |||
d_balance = 1; | |||
} else { // n is right child | |||
assert(n->parent->right == n); | |||
pp = &n->parent->right; | |||
d_balance = -1; | |||
} | |||
} | |||
assert(!pp || *pp == n); | |||
int height_change = 0; | |||
if (!n->left && !n->right) { | |||
// n is a leaf, just remove it | |||
if (pp) { | |||
*pp = NULL; | |||
to_balance = n->parent; | |||
height_change = (!n->parent->left && !n->parent->right) ? -1 : 0; | |||
} | |||
} else if (!n->left) { | |||
// Replace n with right (only) child | |||
if (pp) { | |||
*pp = n->right; | |||
to_balance = n->parent; | |||
} else { | |||
t->root = n->right; | |||
} | |||
n->right->parent = n->parent; | |||
height_change = -1; | |||
} else if (!n->right) { | |||
// Replace n with left (only) child | |||
if (pp) { | |||
*pp = n->left; | |||
to_balance = n->parent; | |||
} else { | |||
t->root = n->left; | |||
} | |||
n->left->parent = n->parent; | |||
height_change = -1; | |||
} else { | |||
// Replace n with in-order successor (leftmost child of right subtree) | |||
ZixTreeNode* replace = n->right; | |||
while (replace->left) { | |||
assert(replace->left->parent == replace); | |||
replace = replace->left; | |||
} | |||
// Remove replace from parent (replace_p) | |||
if (replace->parent->left == replace) { | |||
height_change = replace->parent->right ? 0 : -1; | |||
d_balance = 1; | |||
to_balance = replace->parent; | |||
replace->parent->left = replace->right; | |||
} else { | |||
assert(replace->parent == n); | |||
height_change = replace->parent->left ? 0 : -1; | |||
d_balance = -1; | |||
to_balance = replace->parent; | |||
replace->parent->right = replace->right; | |||
} | |||
if (to_balance == n) { | |||
to_balance = replace; | |||
} | |||
if (replace->right) { | |||
replace->right->parent = replace->parent; | |||
} | |||
replace->balance = n->balance; | |||
// Swap node to delete with replace | |||
if (pp) { | |||
*pp = replace; | |||
} else { | |||
assert(t->root == n); | |||
t->root = replace; | |||
} | |||
replace->parent = n->parent; | |||
replace->left = n->left; | |||
n->left->parent = replace; | |||
replace->right = n->right; | |||
if (n->right) { | |||
n->right->parent = replace; | |||
} | |||
assert(!replace->parent | |||
|| replace->parent->left == replace | |||
|| replace->parent->right == replace); | |||
} | |||
// Rebalance starting at to_balance upwards. | |||
for (ZixTreeNode* i = to_balance; i; i = i->parent) { | |||
i->balance += d_balance; | |||
if (d_balance == 0 || i->balance == -1 || i->balance == 1) { | |||
break; | |||
} | |||
assert(i != n); | |||
i = zix_tree_rebalance(t, i, &height_change); | |||
if (i->balance == 0) { | |||
height_change = -1; | |||
} | |||
if (i->parent) { | |||
if (i == i->parent->left) { | |||
d_balance = height_change * -1; | |||
} else { | |||
assert(i == i->parent->right); | |||
d_balance = height_change; | |||
} | |||
} | |||
} | |||
DUMP(t); | |||
if (t->destroy) { | |||
t->destroy(n->data); | |||
} | |||
free(n); | |||
--t->size; | |||
#ifdef ZIX_TREE_VERIFY | |||
if (!verify(t, t->root)) { | |||
return ZIX_STATUS_ERROR; | |||
} | |||
#endif | |||
return ZIX_STATUS_SUCCESS; | |||
} | |||
ZIX_API ZixStatus | |||
zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti) | |||
{ | |||
ZixTreeNode* n = t->root; | |||
while (n) { | |||
const int cmp = t->cmp(e, n->data, t->cmp_data); | |||
if (cmp == 0) { | |||
break; | |||
} else if (cmp < 0) { | |||
n = n->left; | |||
} else { | |||
n = n->right; | |||
} | |||
} | |||
*ti = n; | |||
return (n) ? ZIX_STATUS_SUCCESS : ZIX_STATUS_NOT_FOUND; | |||
} | |||
ZIX_API void* | |||
zix_tree_get(ZixTreeIter* ti) | |||
{ | |||
return ti ? ti->data : NULL; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_begin(ZixTree* t) | |||
{ | |||
if (!t->root) { | |||
return NULL; | |||
} | |||
ZixTreeNode* n = t->root; | |||
while (n->left) { | |||
n = n->left; | |||
} | |||
return n; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_end(ZixTree* t) | |||
{ | |||
return NULL; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rbegin(ZixTree* t) | |||
{ | |||
if (!t->root) { | |||
return NULL; | |||
} | |||
ZixTreeNode* n = t->root; | |||
while (n->right) { | |||
n = n->right; | |||
} | |||
return n; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rend(ZixTree* t) | |||
{ | |||
return NULL; | |||
} | |||
ZIX_API bool | |||
zix_tree_iter_is_end(ZixTreeIter* i) | |||
{ | |||
return !i; | |||
} | |||
ZIX_API bool | |||
zix_tree_iter_is_rend(ZixTreeIter* i) | |||
{ | |||
return !i; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_next(ZixTreeIter* i) | |||
{ | |||
if (!i) { | |||
return NULL; | |||
} | |||
if (i->right) { | |||
i = i->right; | |||
while (i->left) { | |||
i = i->left; | |||
} | |||
} else { | |||
while (i->parent && i->parent->right == i) { // i is a right child | |||
i = i->parent; | |||
} | |||
i = i->parent; | |||
} | |||
return i; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_prev(ZixTreeIter* i) | |||
{ | |||
if (!i) { | |||
return NULL; | |||
} | |||
if (i->left) { | |||
i = i->left; | |||
while (i->right) { | |||
i = i->right; | |||
} | |||
} else { | |||
while (i->parent && i->parent->left == i) { // i is a left child | |||
i = i->parent; | |||
} | |||
i = i->parent; | |||
} | |||
return i; | |||
} |
@@ -0,0 +1,148 @@ | |||
/* | |||
Copyright 2011 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 ZIX_TREE_H | |||
#define ZIX_TREE_H | |||
#include <stddef.h> | |||
#include "zix/common.h" | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
/** | |||
@addtogroup zix | |||
@{ | |||
@name Tree | |||
@{ | |||
*/ | |||
/** | |||
A balanced binary search tree. | |||
*/ | |||
typedef struct ZixTreeImpl ZixTree; | |||
/** | |||
An iterator over a @ref ZixTree. | |||
*/ | |||
typedef struct ZixTreeNodeImpl ZixTreeIter; | |||
/** | |||
Create a new (empty) tree. | |||
*/ | |||
ZIX_API ZixTree* | |||
zix_tree_new(bool allow_duplicates, | |||
ZixComparator cmp, | |||
void* cmp_data, | |||
ZixDestroyFunc destroy); | |||
/** | |||
Free @a t. | |||
*/ | |||
ZIX_API void | |||
zix_tree_free(ZixTree* t); | |||
/** | |||
Return the number of elements in @a t. | |||
*/ | |||
ZIX_API size_t | |||
zix_tree_size(const ZixTree* t); | |||
/** | |||
Insert the element @a e into @a t and point @a ti at the new element. | |||
*/ | |||
ZIX_API ZixStatus | |||
zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti); | |||
/** | |||
Remove the item pointed at by @a ti from @a t. | |||
*/ | |||
ZIX_API ZixStatus | |||
zix_tree_remove(ZixTree* t, ZixTreeIter* ti); | |||
/** | |||
Set @a ti to an element equal to @a e in @a t. | |||
If no such item exists, @a ti is set to NULL. | |||
*/ | |||
ZIX_API ZixStatus | |||
zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti); | |||
/** | |||
Return the data associated with the given tree item. | |||
*/ | |||
ZIX_API void* | |||
zix_tree_get(ZixTreeIter* ti); | |||
/** | |||
Return an iterator to the first (smallest) element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_begin(ZixTree* t); | |||
/** | |||
Return an iterator the the element one past the last element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_end(ZixTree* t); | |||
/** | |||
Return true iff @a i is an iterator to the end of its tree. | |||
*/ | |||
ZIX_API bool | |||
zix_tree_iter_is_end(ZixTreeIter* i); | |||
/** | |||
Return an iterator to the last (largest) element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rbegin(ZixTree* t); | |||
/** | |||
Return an iterator the the element one before the first element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rend(ZixTree* t); | |||
/** | |||
Return true iff @a i is an iterator to the reverse end of its tree. | |||
*/ | |||
ZIX_API bool | |||
zix_tree_iter_is_rend(ZixTreeIter* i); | |||
/** | |||
Return an iterator that points to the element one past @a i. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_next(ZixTreeIter* i); | |||
/** | |||
Return an iterator that points to the element one before @a i. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_prev(ZixTreeIter* i); | |||
/** | |||
@} | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* ZIX_TREE_H */ |
@@ -0,0 +1,7 @@ | |||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> . | |||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
<http://example.org/lilv-test-plugin> | |||
a lv2:Plugin ; | |||
lv2:binary <test_plugin@SHLIB_EXT@> ; | |||
rdfs:seeAlso <test_plugin.ttl> . |
@@ -0,0 +1,384 @@ | |||
/* | |||
Lilv Test Plugin | |||
Copyright 2011 David Robillard <d@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 <assert.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/lv2plug.in/ns/ext/atom/atom.h" | |||
#include "lv2/lv2plug.in/ns/ext/state/state.h" | |||
#include "lv2/lv2plug.in/ns/ext/urid/urid.h" | |||
#include "lv2/lv2plug.in/ns/lv2core/lv2.h" | |||
#define TEST_URI "http://example.org/lilv-test-plugin" | |||
enum { | |||
TEST_INPUT = 0, | |||
TEST_OUTPUT = 1 | |||
}; | |||
typedef struct { | |||
LV2_URID_Map* map; | |||
struct { | |||
LV2_URID atom_Float; | |||
} uris; | |||
char* tmp_file_path; | |||
char* rec_file_path; | |||
FILE* rec_file; | |||
float* input; | |||
float* output; | |||
unsigned num_runs; | |||
} Test; | |||
static void | |||
cleanup(LV2_Handle instance) | |||
{ | |||
Test* test = (Test*)instance; | |||
if (test->rec_file) { | |||
fclose(test->rec_file); | |||
} | |||
free(test->tmp_file_path); | |||
free(test->rec_file_path); | |||
free(instance); | |||
} | |||
static void | |||
connect_port(LV2_Handle instance, | |||
uint32_t port, | |||
void* data) | |||
{ | |||
Test* test = (Test*)instance; | |||
switch (port) { | |||
case TEST_INPUT: | |||
test->input = (float*)data; | |||
break; | |||
case TEST_OUTPUT: | |||
test->output = (float*)data; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
static LV2_Handle | |||
instantiate(const LV2_Descriptor* descriptor, | |||
double rate, | |||
const char* path, | |||
const LV2_Feature* const* features) | |||
{ | |||
Test* test = (Test*)malloc(sizeof(Test)); | |||
if (!test) { | |||
return NULL; | |||
} | |||
test->map = NULL; | |||
test->input = NULL; | |||
test->output = NULL; | |||
test->num_runs = 0; | |||
test->tmp_file_path = (char*)malloc(L_tmpnam); | |||
test->rec_file_path = NULL; | |||
test->rec_file = NULL; | |||
tmpnam(test->tmp_file_path); | |||
LV2_State_Make_Path* make_path = NULL; | |||
for (int i = 0; features[i]; ++i) { | |||
if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { | |||
test->map = (LV2_URID_Map*)features[i]->data; | |||
test->uris.atom_Float = test->map->map( | |||
test->map->handle, LV2_ATOM__Float); | |||
} else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { | |||
make_path = (LV2_State_Make_Path*)features[i]->data; | |||
} | |||
} | |||
if (!test->map) { | |||
fprintf(stderr, "Host does not support urid:map\n"); | |||
free(test); | |||
return NULL; | |||
} | |||
if (make_path) { | |||
test->rec_file_path = make_path->path(make_path->handle, "recfile"); | |||
if (!(test->rec_file = fopen(test->rec_file_path, "w"))) { | |||
fprintf(stderr, "ERROR: Failed to open rec file\n"); | |||
} | |||
fprintf(test->rec_file, "instantiate\n"); | |||
} | |||
return (LV2_Handle)test; | |||
} | |||
static void | |||
run(LV2_Handle instance, | |||
uint32_t sample_count) | |||
{ | |||
Test* test = (Test*)instance; | |||
*test->output = *test->input; | |||
if (sample_count == 1) { | |||
++test->num_runs; | |||
} else if (sample_count == 2 && test->rec_file) { | |||
// Append to rec file (changes size) | |||
fprintf(test->rec_file, "run\n"); | |||
} else if (sample_count == 3 && test->rec_file) { | |||
// Change the first byte of rec file (doesn't change size) | |||
fseek(test->rec_file, 0, SEEK_SET); | |||
fprintf(test->rec_file, "X"); | |||
fseek(test->rec_file, 0, SEEK_END); | |||
} | |||
} | |||
static uint32_t | |||
map_uri(Test* plugin, const char* uri) | |||
{ | |||
return plugin->map->map(plugin->map->handle, uri); | |||
} | |||
static LV2_State_Status | |||
save(LV2_Handle instance, | |||
LV2_State_Store_Function store, | |||
void* callback_data, | |||
uint32_t flags, | |||
const LV2_Feature* const* features) | |||
{ | |||
Test* plugin = (Test*)instance; | |||
LV2_State_Map_Path* map_path = NULL; | |||
LV2_State_Make_Path* make_path = NULL; | |||
for (int i = 0; features && features[i]; ++i) { | |||
if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { | |||
map_path = (LV2_State_Map_Path*)features[i]->data; | |||
} else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { | |||
make_path = (LV2_State_Make_Path*)features[i]->data; | |||
} | |||
} | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/greeting"), | |||
"hello", | |||
strlen("hello") + 1, | |||
map_uri(plugin, LV2_ATOM__String), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint32_t urid = map_uri(plugin, "http://example.org/urivalue"); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/uri"), | |||
&urid, | |||
sizeof(uint32_t), | |||
map_uri(plugin, LV2_ATOM__URID), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/num-runs"), | |||
&plugin->num_runs, | |||
sizeof(plugin->num_runs), | |||
map_uri(plugin, LV2_ATOM__Int), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const float two = 2.0f; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/two"), | |||
&two, | |||
sizeof(two), | |||
map_uri(plugin, LV2_ATOM__Float), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint32_t affirmative = 1; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/true"), | |||
&affirmative, | |||
sizeof(affirmative), | |||
map_uri(plugin, LV2_ATOM__Bool), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint32_t negative = 0; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/false"), | |||
&negative, | |||
sizeof(negative), | |||
map_uri(plugin, LV2_ATOM__Bool), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint8_t blob[] = "I am a blob of arbitrary data."; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/blob"), | |||
blob, | |||
sizeof(blob), | |||
map_uri(plugin, "http://example.org/SomeUnknownType"), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
if (map_path) { | |||
FILE* file = fopen(plugin->tmp_file_path, "w"); | |||
fprintf(file, "Hello\n"); | |||
fclose(file); | |||
char* apath = map_path->abstract_path(map_path->handle, | |||
plugin->tmp_file_path); | |||
char* apath2 = map_path->abstract_path(map_path->handle, | |||
plugin->tmp_file_path); | |||
if (strcmp(apath, apath2)) { | |||
fprintf(stderr, "ERROR: Path %s != %s\n", apath, apath2); | |||
} | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/extfile"), | |||
apath, | |||
strlen(apath) + 1, | |||
map_uri(plugin, LV2_ATOM__Path), | |||
LV2_STATE_IS_PORTABLE); | |||
free(apath); | |||
free(apath2); | |||
if (plugin->rec_file) { | |||
fflush(plugin->rec_file); | |||
apath = map_path->abstract_path(map_path->handle, | |||
plugin->rec_file_path); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/recfile"), | |||
apath, | |||
strlen(apath) + 1, | |||
map_uri(plugin, LV2_ATOM__Path), | |||
LV2_STATE_IS_PORTABLE); | |||
free(apath); | |||
} | |||
if (make_path) { | |||
char* spath = make_path->path(make_path->handle, "save"); | |||
FILE* sfile = fopen(spath, "w"); | |||
fprintf(sfile, "save"); | |||
fclose(sfile); | |||
apath = map_path->abstract_path(map_path->handle, spath); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/save-file"), | |||
apath, | |||
strlen(apath) + 1, | |||
map_uri(plugin, LV2_ATOM__Path), | |||
LV2_STATE_IS_PORTABLE); | |||
free(apath); | |||
free(spath); | |||
} | |||
} | |||
return LV2_STATE_SUCCESS; | |||
} | |||
static LV2_State_Status | |||
restore(LV2_Handle instance, | |||
LV2_State_Retrieve_Function retrieve, | |||
void* callback_data, | |||
uint32_t flags, | |||
const LV2_Feature* const* features) | |||
{ | |||
Test* plugin = (Test*)instance; | |||
LV2_State_Map_Path* map_path = NULL; | |||
for (int i = 0; features && features[i]; ++i) { | |||
if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { | |||
map_path = (LV2_State_Map_Path*)features[i]->data; | |||
} | |||
} | |||
size_t size; | |||
uint32_t type; | |||
uint32_t valflags; | |||
plugin->num_runs = *(int32_t*)retrieve( | |||
callback_data, | |||
map_uri(plugin, "http://example.org/num-runs"), | |||
&size, &type, &valflags); | |||
if (!map_path) { | |||
return LV2_STATE_ERR_NO_FEATURE; | |||
} | |||
char* apath = (char*)retrieve( | |||
callback_data, | |||
map_uri(plugin, "http://example.org/extfile"), | |||
&size, &type, &valflags); | |||
if (apath) { | |||
char* path = map_path->absolute_path(map_path->handle, apath); | |||
FILE* f = fopen(path, "r"); | |||
char str[8]; | |||
size_t n_read = fread(str, 1, sizeof(str), f); | |||
fclose(f); | |||
if (strncmp(str, "Hello\n", n_read)) { | |||
fprintf(stderr, "error: Restored bad file contents `%s' != `Hello'\n", | |||
str); | |||
} | |||
free(path); | |||
} | |||
apath = (char*)retrieve( | |||
callback_data, | |||
map_uri(plugin, "http://example.org/save-file"), | |||
&size, &type, &valflags); | |||
if (apath) { | |||
char* spath = map_path->absolute_path(map_path->handle, apath); | |||
FILE* sfile = fopen(spath, "r"); | |||
if (!sfile) { | |||
fprintf(stderr, "error: Failed to open save file %s\n", spath); | |||
} else { | |||
fclose(sfile); | |||
} | |||
free(spath); | |||
} else { | |||
fprintf(stderr, "error: Failed to restore save file.\n"); | |||
} | |||
return LV2_STATE_SUCCESS; | |||
} | |||
static const void* | |||
extension_data(const char* uri) | |||
{ | |||
static const LV2_State_Interface state = { save, restore }; | |||
if (!strcmp(uri, LV2_STATE__interface)) { | |||
return &state; | |||
} | |||
return NULL; | |||
} | |||
static const LV2_Descriptor descriptor = { | |||
TEST_URI, | |||
instantiate, | |||
connect_port, | |||
NULL, // activate, | |||
run, | |||
NULL, // deactivate, | |||
cleanup, | |||
extension_data | |||
}; | |||
LV2_SYMBOL_EXPORT | |||
const LV2_Descriptor* lv2_descriptor(uint32_t index) | |||
{ | |||
switch (index) { | |||
case 0: | |||
return &descriptor; | |||
default: | |||
return NULL; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
# Lilv Test Plugin | |||
# Copyright 2011 David Robillard <d@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. | |||
@prefix doap: <http://usefulinc.com/ns/doap#> . | |||
@prefix foaf: <http://xmlns.com/foaf/0.1/> . | |||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> . | |||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . | |||
<http://example.org/lilv-test-plugin> | |||
a lv2:Plugin ; | |||
doap:name "Lilv Test" ; | |||
doap:license <http://opensource.org/licenses/isc> ; | |||
lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#Mapper> ; | |||
lv2:optionalFeature lv2:hardRTCapable ; | |||
lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ; | |||
lv2:port [ | |||
a lv2:InputPort , | |||
lv2:ControlPort ; | |||
lv2:index 0 ; | |||
lv2:symbol "input" ; | |||
lv2:name "Input" | |||
] , [ | |||
a lv2:OutputPort , | |||
lv2:ControlPort ; | |||
lv2:index 1 ; | |||
lv2:symbol "output" ; | |||
lv2:name "Output" | |||
] . |
@@ -0,0 +1,52 @@ | |||
/* | |||
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. | |||
*/ | |||
/** | |||
@file bench.h A simple real-time benchmarking API. | |||
*/ | |||
#ifndef BENCH_H | |||
#define BENCH_H | |||
#define _POSIX_C_SOURCE 199309L | |||
#include <time.h> | |||
#include <sys/time.h> | |||
static inline double | |||
bench_elapsed_s(const struct timespec* start, const struct timespec* end) | |||
{ | |||
return ((end->tv_sec - start->tv_sec) | |||
+ ((end->tv_nsec - start->tv_nsec) * 0.000000001)); | |||
} | |||
static inline struct timespec | |||
bench_start() | |||
{ | |||
struct timespec start_t; | |||
clock_gettime(CLOCK_REALTIME, &start_t); | |||
return start_t; | |||
} | |||
static inline double | |||
bench_end(const struct timespec* start_t) | |||
{ | |||
struct timespec end_t; | |||
clock_gettime(CLOCK_REALTIME, &end_t); | |||
return bench_elapsed_s(start_t, &end_t); | |||
} | |||
#endif /* BENCH_H */ |
@@ -0,0 +1,38 @@ | |||
/* | |||
Copyright 2007-2011 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 <stdio.h> | |||
#include "lilv/lilv.h" | |||
#include "lilv_config.h" | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
LILV_FOREACH(plugins, p, plugins) { | |||
const LilvPlugin* plugin = lilv_plugins_get(plugins, p); | |||
lilv_plugin_get_class(plugin); | |||
} | |||
lilv_world_free(world); | |||
return 0; | |||
} |
@@ -0,0 +1,59 @@ | |||
# Bash auto-completion script written for lv2info and lv2jack. | |||
# Could be adapted to any other program that takes an | |||
# LV2 plugin URI as parameter. | |||
# Updated for Lilv by David Robillard <d@drobilla.net> on 2012-01-08. | |||
# Written by Lars Luthman <lars.luthman@gmail.com> on 2009-10-12. | |||
# No copyright claimed for this script. Do what you want with it. | |||
# For some reason Bash splits the command line not only at whitespace | |||
# but also at ':' signs before putting the parts into COMP_WORDS. | |||
# Since ':' is used in all URIs, which are what we want to complete, | |||
# we have to put the URI back together before we can complete it | |||
# and then cut off the parts we prepended from the completions. | |||
# It probably breaks in some special cases but for most common uses | |||
# it should work fine. | |||
function _lv2info() { | |||
local uri cur opts w wn raw_reply len type | |||
opts=`lv2ls | xargs -n1 echo -n " "` | |||
# This is the last "word", as split by Bash. | |||
cur="${COMP_WORDS[COMP_CWORD]}" | |||
w="$cur" | |||
# Add the previous word while it or this one is a word break character | |||
for i in `seq $(( $COMP_CWORD - 1 )) -1 1`; do | |||
wn="${COMP_WORDS[i]}" | |||
if expr "$COMP_WORDBREAKS" : ".*$wn" > /dev/null; then | |||
if expr "$COMP_WORDBREAKS" : ".*$w" > /dev/null; then | |||
break | |||
fi | |||
fi | |||
w="$wn" | |||
uri="$w$uri" | |||
done | |||
# Check the length of the words we prepend | |||
len=${#uri} | |||
uri="$uri$cur" | |||
raw_reply="$(compgen -W "${opts}" -- ${uri})" | |||
# If we are listing alternatives, just print the full URIs. | |||
type=`echo $COMP_TYPE | awk '{ printf "%c", $1 }'` | |||
if expr "?!@%" : ".*$type" > /dev/null; then | |||
COMPREPLY=( $raw_reply ) | |||
return 0 | |||
fi | |||
# Otherwise, strip the prepended words from all completion suggestions. | |||
COMPREPLY=() | |||
for i in $raw_reply; do | |||
COMPREPLY=( ${COMPREPLY[@]} ${i:len} ) | |||
done | |||
} | |||
complete -F _lv2info lv2info | |||
# And the same for lv2jack. | |||
complete -F _lv2info lv2jack |
@@ -0,0 +1,226 @@ | |||
/* | |||
Copyright 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. | |||
*/ | |||
#define _POSIX_C_SOURCE 199309L | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv/lilv.h" | |||
#include "lv2/lv2plug.in/ns/ext/atom/atom.h" | |||
#include "lilv_config.h" | |||
#include "bench.h" | |||
#include "uri_table.h" | |||
static LilvNode* atom_AtomPort = NULL; | |||
static LilvNode* atom_Sequence = NULL; | |||
static LilvNode* lv2_AudioPort = NULL; | |||
static LilvNode* lv2_CVPort = NULL; | |||
static LilvNode* lv2_ControlPort = NULL; | |||
static LilvNode* lv2_InputPort = NULL; | |||
static LilvNode* lv2_OutputPort = NULL; | |||
static LilvNode* urid_map = NULL; | |||
static bool full_output = false; | |||
static void | |||
print_version(void) | |||
{ | |||
printf( | |||
"lv2bench (lilv) " LILV_VERSION "\n" | |||
"Copyright 2012 David Robillard <http://drobilla.net>\n" | |||
"License: <http://www.opensource.org/licenses/isc-license>\n" | |||
"This is free software: you are free to change and redistribute it.\n" | |||
"There is NO WARRANTY, to the extent permitted by law.\n"); | |||
} | |||
static void | |||
print_usage(void) | |||
{ | |||
printf("lv2bench - Benchmark all installed and supported LV2 plugins.\n"); | |||
printf("Usage: lv2bench [OPTIONS]\n"); | |||
printf("\n"); | |||
printf(" -b BLOCK_SIZE Specify block size, in audio frames.\n"); | |||
printf(" -f, --full Full plottable output.\n"); | |||
printf(" -h, --help Display this help and exit.\n"); | |||
printf(" -n FRAMES Total number of audio frames to process\n"); | |||
printf(" --version Display version information and exit\n"); | |||
} | |||
static double | |||
bench(const LilvPlugin* p, uint32_t sample_count, uint32_t block_size) | |||
{ | |||
URITable uri_table; | |||
uri_table_init(&uri_table); | |||
LV2_URID_Map map = { &uri_table, uri_table_map }; | |||
LV2_Feature map_feature = { LV2_URID_MAP_URI, &map }; | |||
LV2_URID_Unmap unmap = { &uri_table, uri_table_unmap }; | |||
LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap }; | |||
const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL }; | |||
float* const buf = (float*)calloc(block_size * 2, sizeof(float)); | |||
float* const in = buf; | |||
float* const out = buf + block_size; | |||
if (!buf) { | |||
fprintf(stderr, "Out of memory\n"); | |||
return 0.0; | |||
} | |||
LV2_Atom_Sequence seq = { | |||
{ sizeof(LV2_Atom_Sequence_Body), | |||
uri_table_map(&uri_table, LV2_ATOM__Sequence) }, | |||
{ 0, 0 } }; | |||
const char* uri = lilv_node_as_string(lilv_plugin_get_uri(p)); | |||
LilvNodes* required = lilv_plugin_get_required_features(p); | |||
LILV_FOREACH(nodes, i, required) { | |||
const LilvNode* feature = lilv_nodes_get(required, i); | |||
if (!lilv_node_equals(feature, urid_map)) { | |||
fprintf(stderr, "<%s> requires feature <%s>, skipping\n", | |||
uri, lilv_node_as_uri(feature)); | |||
free(buf); | |||
return 0.0; | |||
} | |||
} | |||
LilvInstance* instance = lilv_plugin_instantiate(p, 48000.0, features); | |||
if (!instance) { | |||
fprintf(stderr, "Failed to instantiate <%s>\n", | |||
lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
free(buf); | |||
return 0.0; | |||
} | |||
float* controls = (float*)calloc( | |||
lilv_plugin_get_num_ports(p), sizeof(float)); | |||
lilv_plugin_get_port_ranges_float(p, NULL, NULL, controls); | |||
const uint32_t n_ports = lilv_plugin_get_num_ports(p); | |||
for (uint32_t index = 0; index < n_ports; ++index) { | |||
const LilvPort* port = lilv_plugin_get_port_by_index(p, index); | |||
if (lilv_port_is_a(p, port, lv2_ControlPort)) { | |||
lilv_instance_connect_port(instance, index, &controls[index]); | |||
} else if (lilv_port_is_a(p, port, lv2_AudioPort) || | |||
lilv_port_is_a(p, port, lv2_CVPort)) { | |||
if (lilv_port_is_a(p, port, lv2_InputPort)) { | |||
lilv_instance_connect_port(instance, index, in); | |||
} else if (lilv_port_is_a(p, port, lv2_OutputPort)) { | |||
lilv_instance_connect_port(instance, index, out); | |||
} else { | |||
fprintf(stderr, "<%s> port %d neither input nor output, skipping\n", | |||
uri, index); | |||
lilv_instance_free(instance); | |||
free(buf); | |||
free(controls); | |||
return 0.0; | |||
} | |||
} else if (lilv_port_is_a(p, port, atom_AtomPort)) { | |||
lilv_instance_connect_port(instance, index, &seq); | |||
} else { | |||
fprintf(stderr, "<%s> port %d has unknown type, skipping\n", | |||
uri, index); | |||
lilv_instance_free(instance); | |||
free(buf); | |||
free(controls); | |||
return 0.0; | |||
} | |||
} | |||
lilv_instance_activate(instance); | |||
struct timespec ts = bench_start(); | |||
for (uint32_t i = 0; i < (sample_count / block_size); ++i) { | |||
lilv_instance_run(instance, block_size); | |||
} | |||
const double elapsed = bench_end(&ts); | |||
lilv_instance_deactivate(instance); | |||
lilv_instance_free(instance); | |||
uri_table_destroy(&uri_table); | |||
if (full_output) { | |||
printf("%d %d ", block_size, sample_count); | |||
} | |||
printf("%lf %s\n", elapsed, uri); | |||
free(buf); | |||
free(controls); | |||
return elapsed; | |||
} | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
uint32_t block_size = 512; | |||
uint32_t sample_count = (1 << 19); | |||
for (int i = 1; i < argc; ++i) { | |||
if (!strcmp(argv[i], "--version")) { | |||
print_version(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "--help")) { | |||
print_usage(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "-f")) { | |||
full_output = true; | |||
} else if (!strcmp(argv[i], "-n") && (i + 1 < argc)) { | |||
sample_count = atoi(argv[++i]); | |||
} else if (!strcmp(argv[i], "-b") && (i + 1 < argc)) { | |||
block_size = atoi(argv[++i]); | |||
} else { | |||
print_usage(); | |||
return 1; | |||
} | |||
} | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); | |||
atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence); | |||
lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort); | |||
lv2_CVPort = lilv_new_uri(world, LV2_CORE__CVPort); | |||
lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort); | |||
lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort); | |||
lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort); | |||
urid_map = lilv_new_uri(world, LV2_URID__map); | |||
if (full_output) { | |||
printf("# Block Samples Time Plugin\n"); | |||
} | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
LILV_FOREACH(plugins, i, plugins) { | |||
bench(lilv_plugins_get(plugins, i), sample_count, block_size); | |||
} | |||
lilv_node_free(urid_map); | |||
lilv_node_free(lv2_OutputPort); | |||
lilv_node_free(lv2_InputPort); | |||
lilv_node_free(lv2_ControlPort); | |||
lilv_node_free(lv2_CVPort); | |||
lilv_node_free(lv2_AudioPort); | |||
lilv_node_free(atom_Sequence); | |||
lilv_node_free(atom_AtomPort); | |||
lilv_world_free(world); | |||
return 0; | |||
} |
@@ -0,0 +1,437 @@ | |||
/* | |||
Copyright 2007-2011 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 <float.h> | |||
#include <math.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" | |||
#include "lv2/lv2plug.in/ns/ext/presets/presets.h" | |||
#include "lv2/lv2plug.in/ns/ext/event/event.h" | |||
#include "lilv/lilv.h" | |||
#include "lilv_config.h" | |||
#ifdef _MSC_VER | |||
# define isnan _isnan | |||
#endif | |||
LilvNode* applies_to_pred = NULL; | |||
LilvNode* control_class = NULL; | |||
LilvNode* event_class = NULL; | |||
LilvNode* group_pred = NULL; | |||
LilvNode* label_pred = NULL; | |||
LilvNode* preset_class = NULL; | |||
LilvNode* designation_pred = NULL; | |||
LilvNode* supports_event_pred = NULL; | |||
static void | |||
print_port(const LilvPlugin* p, | |||
uint32_t index, | |||
float* mins, | |||
float* maxes, | |||
float* defaults) | |||
{ | |||
const LilvPort* port = lilv_plugin_get_port_by_index(p, index); | |||
printf("\n\tPort %d:\n", index); | |||
if (!port) { | |||
printf("\t\tERROR: Illegal/nonexistent port\n"); | |||
return; | |||
} | |||
bool first = true; | |||
const LilvNodes* classes = lilv_port_get_classes(p, port); | |||
printf("\t\tType: "); | |||
LILV_FOREACH(nodes, i, classes) { | |||
const LilvNode* value = lilv_nodes_get(classes, i); | |||
if (!first) { | |||
printf("\n\t\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(value)); | |||
first = false; | |||
} | |||
if (lilv_port_is_a(p, port, event_class)) { | |||
LilvNodes* supported = lilv_port_get_value( | |||
p, port, supports_event_pred); | |||
if (lilv_nodes_size(supported) > 0) { | |||
printf("\n\t\tSupported events:\n"); | |||
LILV_FOREACH(nodes, i, supported) { | |||
const LilvNode* value = lilv_nodes_get(supported, i); | |||
printf("\t\t\t%s\n", lilv_node_as_uri(value)); | |||
} | |||
} | |||
lilv_nodes_free(supported); | |||
} | |||
LilvScalePoints* points = lilv_port_get_scale_points(p, port); | |||
if (points) | |||
printf("\n\t\tScale Points:\n"); | |||
LILV_FOREACH(scale_points, i, points) { | |||
const LilvScalePoint* point = lilv_scale_points_get(points, i); | |||
printf("\t\t\t%s = \"%s\"\n", | |||
lilv_node_as_string(lilv_scale_point_get_value(point)), | |||
lilv_node_as_string(lilv_scale_point_get_label(point))); | |||
} | |||
lilv_scale_points_free(points); | |||
const LilvNode* sym = lilv_port_get_symbol(p, port); | |||
printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym)); | |||
LilvNode* name = lilv_port_get_name(p, port); | |||
printf("\t\tName: %s\n", lilv_node_as_string(name)); | |||
lilv_node_free(name); | |||
LilvNodes* groups = lilv_port_get_value(p, port, group_pred); | |||
if (lilv_nodes_size(groups) > 0) { | |||
printf("\t\tGroup: %s\n", | |||
lilv_node_as_string(lilv_nodes_get_first(groups))); | |||
} | |||
lilv_nodes_free(groups); | |||
LilvNodes* designations = lilv_port_get_value(p, port, designation_pred); | |||
if (lilv_nodes_size(designations) > 0) { | |||
printf("\t\tDesignation: %s\n", | |||
lilv_node_as_string(lilv_nodes_get_first(designations))); | |||
} | |||
lilv_nodes_free(designations); | |||
if (lilv_port_is_a(p, port, control_class)) { | |||
if (!isnan(mins[index])) | |||
printf("\t\tMinimum: %f\n", mins[index]); | |||
if (!isnan(mins[index])) | |||
printf("\t\tMaximum: %f\n", maxes[index]); | |||
if (!isnan(mins[index])) | |||
printf("\t\tDefault: %f\n", defaults[index]); | |||
} | |||
LilvNodes* properties = lilv_port_get_properties(p, port); | |||
if (lilv_nodes_size(properties) > 0) | |||
printf("\t\tProperties: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, properties) { | |||
if (!first) { | |||
printf("\t\t "); | |||
} | |||
printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i))); | |||
first = false; | |||
} | |||
if (lilv_nodes_size(properties) > 0) | |||
printf("\n"); | |||
lilv_nodes_free(properties); | |||
} | |||
static void | |||
print_plugin(LilvWorld* world, | |||
const LilvPlugin* p) | |||
{ | |||
LilvNode* val = NULL; | |||
printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
val = lilv_plugin_get_name(p); | |||
if (val) { | |||
printf("\tName: %s\n", lilv_node_as_string(val)); | |||
lilv_node_free(val); | |||
} | |||
const LilvPluginClass* pclass = lilv_plugin_get_class(p); | |||
const LilvNode* class_label = lilv_plugin_class_get_label(pclass); | |||
if (class_label) { | |||
printf("\tClass: %s\n", lilv_node_as_string(class_label)); | |||
} | |||
val = lilv_plugin_get_author_name(p); | |||
if (val) { | |||
printf("\tAuthor: %s\n", lilv_node_as_string(val)); | |||
lilv_node_free(val); | |||
} | |||
val = lilv_plugin_get_author_email(p); | |||
if (val) { | |||
printf("\tAuthor Email: %s\n", lilv_node_as_uri(val)); | |||
lilv_node_free(val); | |||
} | |||
val = lilv_plugin_get_author_homepage(p); | |||
if (val) { | |||
printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val)); | |||
lilv_node_free(val); | |||
} | |||
if (lilv_plugin_has_latency(p)) { | |||
uint32_t latency_port = lilv_plugin_get_latency_port_index(p); | |||
printf("\tHas latency: yes, reported by port %d\n", latency_port); | |||
} else { | |||
printf("\tHas latency: no\n"); | |||
} | |||
printf("\tBundle: %s\n", | |||
lilv_node_as_uri(lilv_plugin_get_bundle_uri(p))); | |||
const LilvNode* binary_uri = lilv_plugin_get_library_uri(p); | |||
if (binary_uri) { | |||
printf("\tBinary: %s\n", | |||
lilv_node_as_uri(lilv_plugin_get_library_uri(p))); | |||
} | |||
LilvUIs* uis = lilv_plugin_get_uis(p); | |||
if (lilv_nodes_size(uis) > 0) { | |||
printf("\tUIs:\n"); | |||
LILV_FOREACH(uis, i, uis) { | |||
const LilvUI* ui = lilv_uis_get(uis, i); | |||
printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); | |||
const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); | |||
const LilvNodes* types = lilv_ui_get_classes(ui); | |||
LILV_FOREACH(nodes, t, types) { | |||
printf("\t\t\tClass: %s\n", | |||
lilv_node_as_uri(lilv_nodes_get(types, t))); | |||
} | |||
if (binary) | |||
printf("\t\t\tBinary: %s\n", binary); | |||
printf("\t\t\tBundle: %s\n", | |||
lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); | |||
} | |||
} | |||
lilv_uis_free(uis); | |||
printf("\tData URIs: "); | |||
const LilvNodes* data_uris = lilv_plugin_get_data_uris(p); | |||
bool first = true; | |||
LILV_FOREACH(nodes, i, data_uris) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i))); | |||
first = false; | |||
} | |||
printf("\n"); | |||
/* Required Features */ | |||
LilvNodes* features = lilv_plugin_get_required_features(p); | |||
if (features) | |||
printf("\tRequired Features: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, features) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); | |||
first = false; | |||
} | |||
if (features) | |||
printf("\n"); | |||
lilv_nodes_free(features); | |||
/* Optional Features */ | |||
features = lilv_plugin_get_optional_features(p); | |||
if (features) | |||
printf("\tOptional Features: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, features) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); | |||
first = false; | |||
} | |||
if (features) | |||
printf("\n"); | |||
lilv_nodes_free(features); | |||
/* Extension Data */ | |||
LilvNodes* data = lilv_plugin_get_extension_data(p); | |||
if (data) | |||
printf("\tExtension Data: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, data) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); | |||
first = false; | |||
} | |||
if (data) | |||
printf("\n"); | |||
lilv_nodes_free(data); | |||
/* Presets */ | |||
LilvNodes* presets = lilv_plugin_get_related(p, preset_class); | |||
if (presets) | |||
printf("\tPresets: \n"); | |||
LILV_FOREACH(nodes, i, presets) { | |||
const LilvNode* preset = lilv_nodes_get(presets, i); | |||
lilv_world_load_resource(world, preset); | |||
LilvNodes* titles = lilv_world_find_nodes( | |||
world, preset, label_pred, NULL); | |||
if (titles) { | |||
const LilvNode* title = lilv_nodes_get_first(titles); | |||
printf("\t %s\n", lilv_node_as_string(title)); | |||
lilv_nodes_free(titles); | |||
} else { | |||
fprintf(stderr, "Preset <%s> has no rdfs:label\n", | |||
lilv_node_as_string(lilv_nodes_get(presets, i))); | |||
} | |||
} | |||
lilv_nodes_free(presets); | |||
/* Ports */ | |||
const uint32_t num_ports = lilv_plugin_get_num_ports(p); | |||
float* mins = (float*)calloc(num_ports, sizeof(float)); | |||
float* maxes = (float*)calloc(num_ports, sizeof(float)); | |||
float* defaults = (float*)calloc(num_ports, sizeof(float)); | |||
lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults); | |||
for (uint32_t i = 0; i < num_ports; ++i) | |||
print_port(p, i, mins, maxes, defaults); | |||
free(mins); | |||
free(maxes); | |||
free(defaults); | |||
} | |||
static void | |||
print_version(void) | |||
{ | |||
printf( | |||
"lv2info (lilv) " LILV_VERSION "\n" | |||
"Copyright 2007-2011 David Robillard <http://drobilla.net>\n" | |||
"License: <http://www.opensource.org/licenses/isc-license>\n" | |||
"This is free software: you are free to change and redistribute it.\n" | |||
"There is NO WARRANTY, to the extent permitted by law.\n"); | |||
} | |||
static void | |||
print_usage(void) | |||
{ | |||
printf( | |||
"Usage: lv2info [OPTION]... PLUGIN_URI\n" | |||
"Print information about an installed LV2 plugin.\n\n" | |||
" -p FILE Write Turtle description of plugin to FILE\n" | |||
" -m FILE Add record of plugin to manifest FILE\n" | |||
" --help Display this help and exit\n" | |||
" --version Display version information and exit\n\n" | |||
"For -p and -m, Turtle files are appended to (not overwritten),\n" | |||
"and @prefix directives are only written if the file was empty.\n" | |||
"This allows several plugins to be added to a single file.\n"); | |||
} | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
if (argc == 1) { | |||
print_usage(); | |||
return 1; | |||
} | |||
const char* plugin_file = NULL; | |||
const char* manifest_file = NULL; | |||
const char* plugin_uri = NULL; | |||
for (int i = 1; i < argc; ++i) { | |||
if (!strcmp(argv[i], "--version")) { | |||
print_version(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "--help")) { | |||
print_usage(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "-p")) { | |||
plugin_file = argv[++i]; | |||
} else if (!strcmp(argv[i], "-m")) { | |||
manifest_file = argv[++i]; | |||
} else if (argv[i][0] == '-') { | |||
print_usage(); | |||
return 1; | |||
} else if (i == argc - 1) { | |||
plugin_uri = argv[i]; | |||
} | |||
} | |||
int ret = 0; | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
LilvNode* uri = lilv_new_uri(world, plugin_uri); | |||
if (!uri) { | |||
fprintf(stderr, "Invalid plugin URI\n"); | |||
lilv_world_free(world); | |||
return 1; | |||
} | |||
applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo); | |||
control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); | |||
event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); | |||
group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group); | |||
label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); | |||
preset_class = lilv_new_uri(world, LV2_PRESETS__Preset); | |||
designation_pred = lilv_new_uri(world, LV2_CORE__designation); | |||
supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent); | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri); | |||
if (p && plugin_file) { | |||
LilvNode* base = lilv_new_uri(world, plugin_file); | |||
FILE* plugin_fd = fopen(plugin_file, "a"); | |||
lilv_plugin_write_description(world, p, base, plugin_fd); | |||
fclose(plugin_fd); | |||
if (manifest_file) { | |||
FILE* manifest_fd = fopen(manifest_file, "a"); | |||
lilv_plugin_write_manifest_entry( | |||
world, p, base, manifest_fd, plugin_file); | |||
fclose(manifest_fd); | |||
} | |||
lilv_node_free(base); | |||
} else if (p) { | |||
print_plugin(world, p); | |||
} else { | |||
fprintf(stderr, "Plugin not found.\n"); | |||
} | |||
ret = (p != NULL ? 0 : -1); | |||
lilv_node_free(uri); | |||
lilv_node_free(supports_event_pred); | |||
lilv_node_free(designation_pred); | |||
lilv_node_free(preset_class); | |||
lilv_node_free(label_pred); | |||
lilv_node_free(group_pred); | |||
lilv_node_free(event_class); | |||
lilv_node_free(control_class); | |||
lilv_node_free(applies_to_pred); | |||
lilv_world_free(world); | |||
return ret; | |||
} | |||
@@ -0,0 +1,93 @@ | |||
/* | |||
Copyright 2007-2011 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 <stdio.h> | |||
#include <string.h> | |||
#include "lilv/lilv.h" | |||
#include "lilv_config.h" | |||
static void | |||
list_plugins(const LilvPlugins* list, bool show_names) | |||
{ | |||
LILV_FOREACH(plugins, i, list) { | |||
const LilvPlugin* p = lilv_plugins_get(list, i); | |||
if (show_names) { | |||
LilvNode* n = lilv_plugin_get_name(p); | |||
printf("%s\n", lilv_node_as_string(n)); | |||
lilv_node_free(n); | |||
} else { | |||
printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
} | |||
} | |||
} | |||
static void | |||
print_version(void) | |||
{ | |||
printf( | |||
"lv2ls (lilv) " LILV_VERSION "\n" | |||
"Copyright 2007-2011 David Robillard <http://drobilla.net>\n" | |||
"License: <http://www.opensource.org/licenses/isc-license>\n" | |||
"This is free software: you are free to change and redistribute it.\n" | |||
"There is NO WARRANTY, to the extent permitted by law.\n"); | |||
} | |||
static void | |||
print_usage(void) | |||
{ | |||
printf("Usage: lv2ls [OPTION]...\n"); | |||
printf("List all installed LV2 plugins.\n"); | |||
printf("\n"); | |||
printf(" -n, --names Show names instead of URIs\n"); | |||
printf(" --help Display this help and exit\n"); | |||
printf(" --version Display version information and exit\n"); | |||
printf("\n"); | |||
printf("The environment variable LV2_PATH can be used to control where\n"); | |||
printf("this (and all other lilv based LV2 hosts) will search for plugins.\n"); | |||
} | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
bool show_names = false; | |||
for (int i = 1; i < argc; ++i) { | |||
if (!strcmp(argv[i], "--names") || !strcmp(argv[i], "-n")) { | |||
show_names = true; | |||
} else if (!strcmp(argv[i], "--version")) { | |||
print_version(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "--help")) { | |||
print_usage(); | |||
return 0; | |||
} else { | |||
print_usage(); | |||
return 1; | |||
} | |||
} | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
list_plugins(plugins, show_names); | |||
lilv_world_free(world); | |||
return 0; | |||
} |
@@ -0,0 +1,73 @@ | |||
/* | |||
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. | |||
*/ | |||
/** | |||
@file uri_table.h A toy URI map/unmap implementation. | |||
This file contains function definitions and must only be included once. | |||
*/ | |||
#ifndef URI_TABLE_H | |||
#define URI_TABLE_H | |||
typedef struct { | |||
char** uris; | |||
size_t n_uris; | |||
} URITable; | |||
static void | |||
uri_table_init(URITable* table) | |||
{ | |||
table->uris = NULL; | |||
table->n_uris = 0; | |||
} | |||
static void | |||
uri_table_destroy(URITable* table) | |||
{ | |||
free(table->uris); | |||
} | |||
static LV2_URID | |||
uri_table_map(LV2_URID_Map_Handle handle, | |||
const char* uri) | |||
{ | |||
URITable* table = (URITable*)handle; | |||
for (size_t i = 0; i < table->n_uris; ++i) { | |||
if (!strcmp(table->uris[i], uri)) { | |||
return i + 1; | |||
} | |||
} | |||
const size_t len = strlen(uri); | |||
table->uris = (char**)realloc(table->uris, ++table->n_uris * sizeof(char*)); | |||
table->uris[table->n_uris - 1] = malloc(len + 1); | |||
memcpy(table->uris[table->n_uris - 1], uri, len + 1); | |||
return table->n_uris; | |||
} | |||
static const char* | |||
uri_table_unmap(LV2_URID_Map_Handle handle, | |||
LV2_URID urid) | |||
{ | |||
URITable* table = (URITable*)handle; | |||
if (urid > 0 && urid <= table->n_uris) { | |||
return table->uris[urid - 1]; | |||
} | |||
return NULL; | |||
} | |||
#endif /* URI_TABLE_H */ |
@@ -0,0 +1,378 @@ | |||
#!/usr/bin/env python | |||
import os | |||
import sys | |||
import subprocess | |||
from waflib.extras import autowaf as autowaf | |||
import waflib.Options as Options | |||
# Version of this package (even if built as a child) | |||
LILV_VERSION = '0.14.4' | |||
LILV_MAJOR_VERSION = '0' | |||
# Library version (UNIX style major, minor, micro) | |||
# major increment <=> incompatible changes | |||
# minor increment <=> compatible changes (additions) | |||
# micro increment <=> no interface changes | |||
# Lilv uses the same version number for both library and package | |||
LILV_LIB_VERSION = LILV_VERSION | |||
# Variables for 'waf dist' | |||
APPNAME = 'lilv' | |||
VERSION = LILV_VERSION | |||
# Mandatory variables | |||
top = '.' | |||
out = 'build' | |||
def options(opt): | |||
opt.load('compiler_c') | |||
opt.load('compiler_cxx') | |||
opt.load('python') | |||
autowaf.set_options(opt) | |||
opt.add_option('--no-utils', action='store_true', dest='no_utils', | |||
help='Do not build command line utilities') | |||
opt.add_option('--bindings', action='store_true', dest='bindings', | |||
help='Build python bindings') | |||
opt.add_option('--dyn-manifest', action='store_true', dest='dyn_manifest', | |||
help='Build support for dynamic manifests') | |||
opt.add_option('--test', action='store_true', dest='build_tests', | |||
help='Build unit tests') | |||
opt.add_option('--no-bash-completion', action='store_true', | |||
dest='no_bash_completion', | |||
help='Do not install bash completion script in CONFIGDIR') | |||
opt.add_option('--static', action='store_true', dest='static', | |||
help='Build static library') | |||
opt.add_option('--no-shared', action='store_true', dest='no_shared', | |||
help='Do not build shared library') | |||
opt.add_option('--static-progs', action='store_true', dest='static_progs', | |||
help='Build programs as static binaries') | |||
opt.add_option('--default-lv2-path', type='string', default='', | |||
dest='default_lv2_path', | |||
help='Default LV2 path to use if LV2_PATH is unset') | |||
def configure(conf): | |||
conf.load('compiler_c') | |||
if Options.options.bindings: | |||
try: | |||
conf.load('swig') | |||
conf.load('python') | |||
conf.load('compiler_cxx') | |||
conf.check_python_headers() | |||
autowaf.define(conf, 'LILV_PYTHON', 1); | |||
except: | |||
pass | |||
autowaf.configure(conf) | |||
autowaf.set_c99_mode(conf) | |||
autowaf.display_header('Lilv Configuration') | |||
conf.env.BASH_COMPLETION = not Options.options.no_bash_completion | |||
conf.env.BUILD_TESTS = Options.options.build_tests | |||
conf.env.BUILD_UTILS = not Options.options.no_utils | |||
conf.env.BUILD_SHARED = not Options.options.no_shared | |||
conf.env.STATIC_PROGS = Options.options.static_progs | |||
conf.env.BUILD_STATIC = (Options.options.static or | |||
Options.options.static_progs) | |||
if not conf.env.BUILD_SHARED and not conf.env.BUILD_STATIC: | |||
conf.fatal('Neither a shared nor a static build requested') | |||
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2', | |||
atleast_version='1.0.0', mandatory=True) | |||
autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', | |||
atleast_version='0.14.0', mandatory=True) | |||
autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD', | |||
atleast_version='0.8.0', mandatory=True) | |||
autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM', | |||
atleast_version='0.2.0', mandatory=True) | |||
autowaf.define(conf, 'LILV_NEW_LV2', 1) # New LV2 discovery API | |||
defines = ['_POSIX_C_SOURCE', '_BSD_SOURCE'] | |||
if Options.platform == 'darwin': | |||
defines += ['_DARWIN_C_SOURCE'] | |||
# Check for gcov library (for test coverage) | |||
if conf.env.BUILD_TESTS: | |||
conf.check_cc(lib='gcov', | |||
define_name='HAVE_GCOV', | |||
mandatory=False) | |||
conf.check_cc(function_name='flock', | |||
header_name='sys/file.h', | |||
defines=defines, | |||
define_name='HAVE_FLOCK', | |||
mandatory=False) | |||
conf.check_cc(function_name='fileno', | |||
header_name='stdio.h', | |||
defines=defines, | |||
define_name='HAVE_FILENO', | |||
mandatory=False) | |||
conf.check_cc(function_name='clock_gettime', | |||
header_name=['sys/time.h','time.h'], | |||
defines=['_POSIX_C_SOURCE=199309L'], | |||
define_name='HAVE_CLOCK_GETTIME', | |||
uselib_store='CLOCK_GETTIME', | |||
lib=['rt'], | |||
mandatory=False) | |||
autowaf.define(conf, 'LILV_VERSION', LILV_VERSION) | |||
if Options.options.dyn_manifest: | |||
autowaf.define(conf, 'LILV_DYN_MANIFEST', 1) | |||
lilv_path_sep = ':' | |||
lilv_dir_sep = '/' | |||
if sys.platform == 'win32': | |||
lilv_path_sep = ';' | |||
lilv_dir_sep = '\\\\' | |||
autowaf.define(conf, 'LILV_PATH_SEP', lilv_path_sep) | |||
autowaf.define(conf, 'LILV_DIR_SEP', lilv_dir_sep) | |||
# Set default LV2 path | |||
lv2_path = Options.options.default_lv2_path | |||
if lv2_path == '': | |||
if Options.platform == 'darwin': | |||
lv2_path = lilv_path_sep.join(['~/Library/Audio/Plug-Ins/LV2', | |||
'~/.lv2', | |||
'/usr/local/lib/lv2', | |||
'/usr/lib/lv2', | |||
'/Library/Audio/Plug-Ins/LV2']) | |||
elif Options.platform == 'haiku': | |||
lv2_path = lilv_path_sep.join(['~/.lv2', | |||
'/boot/common/add-ons/lv2']) | |||
elif Options.platform == 'win32': | |||
lv2_path = lilv_path_sep.join(['%APPDATA%\\\\LV2', | |||
'%COMMONPROGRAMFILES%\\\\LV2']) | |||
else: | |||
libdirname = os.path.basename(conf.env.LIBDIR) | |||
lv2_path = lilv_path_sep.join(['~/.lv2', | |||
'/usr/%s/lv2' % libdirname, | |||
'/usr/local/%s/lv2' % libdirname]) | |||
autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', lv2_path) | |||
conf.env.LIB_LILV = ['lilv-%s' % LILV_MAJOR_VERSION] | |||
conf.write_config_header('lilv_config.h', remove=False) | |||
autowaf.display_msg(conf, 'Default LV2_PATH', | |||
conf.env.LILV_DEFAULT_LV2_PATH) | |||
autowaf.display_msg(conf, 'Utilities', | |||
bool(conf.env.BUILD_UTILS)) | |||
autowaf.display_msg(conf, 'Unit tests', | |||
bool(conf.env.BUILD_TESTS)) | |||
autowaf.display_msg(conf, 'Dynamic manifest support', | |||
bool(conf.env.LILV_DYN_MANIFEST)) | |||
autowaf.display_msg(conf, 'Python bindings', | |||
conf.is_defined('LILV_PYTHON')) | |||
conf.undefine('LILV_DEFAULT_LV2_PATH') # Cmd line errors with VC++ | |||
print('') | |||
def build_util(bld, name, defines): | |||
obj = bld(features = 'c cprogram', | |||
source = name + '.c', | |||
includes = ['.', './src', './utils'], | |||
use = 'liblilv', | |||
target = name, | |||
defines = defines, | |||
install_path = '${BINDIR}') | |||
if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS: | |||
obj.use = 'liblilv_static' | |||
if bld.env.STATIC_PROGS: | |||
if not bld.env.MSVC_COMPILER: | |||
obj.lib = ['m'] | |||
obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER | |||
obj.linkflags = ['-static', '-Wl,--start-group'] | |||
return obj | |||
def build(bld): | |||
# C/C++ Headers | |||
includedir = '${INCLUDEDIR}/lilv-%s/lilv' % LILV_MAJOR_VERSION | |||
bld.install_files(includedir, bld.path.ant_glob('lilv/*.h')) | |||
bld.install_files(includedir, bld.path.ant_glob('lilv/*.hpp')) | |||
# Pkgconfig file | |||
autowaf.build_pc(bld, 'LILV', LILV_VERSION, LILV_MAJOR_VERSION, [], | |||
{'LILV_MAJOR_VERSION' : LILV_MAJOR_VERSION, | |||
'LILV_PKG_DEPS' : 'lv2 serd-0 sord-0 sratom-0'}) | |||
lib_source = ''' | |||
src/collections.c | |||
src/instance.c | |||
src/lib.c | |||
src/node.c | |||
src/plugin.c | |||
src/pluginclass.c | |||
src/port.c | |||
src/query.c | |||
src/scalepoint.c | |||
src/state.c | |||
src/ui.c | |||
src/util.c | |||
src/world.c | |||
src/zix/tree.c | |||
'''.split() | |||
lib = ['dl'] | |||
libflags = ['-fvisibility=hidden'] | |||
defines = [] | |||
if sys.platform == 'win32': | |||
lib = [] | |||
if bld.env.MSVC_COMPILER: | |||
libflags = [] | |||
defines = ['snprintf=_snprintf'] | |||
elif sys.platform.find('bsd') > 0: | |||
lib = [] | |||
# Shared Library | |||
if bld.env.BUILD_SHARED: | |||
obj = bld(features = 'c cshlib', | |||
export_includes = ['.'], | |||
source = lib_source, | |||
includes = ['.', './src'], | |||
name = 'liblilv', | |||
target = 'lilv-%s' % LILV_MAJOR_VERSION, | |||
vnum = LILV_LIB_VERSION, | |||
install_path = '${LIBDIR}', | |||
defines = ['LILV_SHARED', 'LILV_INTERNAL'], | |||
cflags = libflags, | |||
lib = lib) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
# Static library | |||
if bld.env.BUILD_STATIC: | |||
obj = bld(features = 'c cstlib', | |||
export_includes = ['.'], | |||
source = lib_source, | |||
includes = ['.', './src'], | |||
name = 'liblilv_static', | |||
target = 'lilv-%s' % LILV_MAJOR_VERSION, | |||
vnum = LILV_LIB_VERSION, | |||
install_path = '${LIBDIR}', | |||
defines = defines + ['LILV_INTERNAL']) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
if bld.env.BUILD_TESTS: | |||
test_libs = lib | |||
test_cflags = [''] | |||
if bld.is_defined('HAVE_GCOV'): | |||
test_libs += ['gcov'] | |||
test_cflags += ['-fprofile-arcs', '-ftest-coverage'] | |||
# Test plugin library | |||
penv = bld.env.derive() | |||
shlib_pattern = penv.cshlib_PATTERN | |||
if shlib_pattern.startswith('lib'): | |||
shlib_pattern = shlib_pattern[3:] | |||
penv.cshlib_PATTERN = shlib_pattern | |||
shlib_ext = shlib_pattern[shlib_pattern.rfind('.'):] | |||
obj = bld(features = 'c cshlib', | |||
env = penv, | |||
source = 'test/test_plugin.c', | |||
name = 'test_plugin', | |||
target = 'test/test_plugin.lv2/test_plugin', | |||
install_path = None, | |||
defines = defines, | |||
cflags = test_cflags, | |||
lib = test_libs, | |||
uselib = 'LV2') | |||
# Test plugin data files | |||
for i in [ 'manifest.ttl.in', 'test_plugin.ttl.in' ]: | |||
bld(features = 'subst', | |||
source = 'test/' + i, | |||
target = 'test/test_plugin.lv2/' + i.replace('.in', ''), | |||
install_path = None, | |||
SHLIB_EXT = shlib_ext) | |||
# Static profiled library (for unit test code coverage) | |||
obj = bld(features = 'c cstlib', | |||
source = lib_source, | |||
includes = ['.', './src'], | |||
name = 'liblilv_profiled', | |||
target = 'lilv_profiled', | |||
install_path = None, | |||
defines = defines + ['LILV_INTERNAL'], | |||
cflags = test_cflags, | |||
lib = test_libs) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
# Unit test program | |||
bpath = os.path.abspath(os.path.join(out, 'test', 'test_plugin.lv2')) | |||
bpath = bpath.replace('\\', '/') | |||
obj = bld(features = 'c cprogram', | |||
source = 'test/lilv_test.c', | |||
includes = ['.', './src'], | |||
use = 'liblilv_profiled', | |||
uselib = 'SORD LV2', | |||
lib = test_libs, | |||
target = 'test/lilv_test', | |||
install_path = None, | |||
defines = defines + ['LILV_TEST_BUNDLE=\"%s/\"' % bpath], | |||
cflags = test_cflags) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
# Utilities | |||
if bld.env.BUILD_UTILS: | |||
utils = ''' | |||
utils/lilv-bench | |||
utils/lv2info | |||
utils/lv2ls | |||
''' | |||
for i in utils.split(): | |||
build_util(bld, i, defines) | |||
# lv2bench (less portable than other utilities) | |||
if bld.is_defined('HAVE_CLOCK_GETTIME'): | |||
obj = build_util(bld, 'utils/lv2bench', defines) | |||
if not bld.env.MSVC_COMPILER: | |||
obj.lib = ['rt'] | |||
# Documentation | |||
autowaf.build_dox(bld, 'LILV', LILV_VERSION, top, out) | |||
# Man pages | |||
bld.install_files('${MANDIR}/man1', bld.path.ant_glob('doc/*.1')) | |||
# Bash completion | |||
if bld.env.BASH_COMPLETION: | |||
bld.install_as( | |||
'${SYSCONFDIR}/bash_completion.d/lilv', 'utils/lilv.bash_completion') | |||
if bld.is_defined('LILV_PYTHON'): | |||
# Python Wrapper | |||
obj = bld(features = 'cxx cxxshlib pyext', | |||
source = 'bindings/lilv.i', | |||
target = 'bindings/_lilv', | |||
includes = ['..'], | |||
swig_flags = '-c++ -python -Wall -I.. -llilv -features autodoc=1', | |||
use = 'liblilv') | |||
autowaf.use_lib(bld, obj, 'LILV') | |||
bld.install_files('${PYTHONDIR}', 'bindings/lilv.py') | |||
bld.add_post_fun(autowaf.run_ldconfig) | |||
if bld.env.DOCS: | |||
bld.add_post_fun(fix_docs) | |||
def fix_docs(ctx): | |||
if ctx.cmd == 'build': | |||
autowaf.make_simple_dox(APPNAME) | |||
def upload_docs(ctx): | |||
os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/lilv/') | |||
def test(ctx): | |||
autowaf.pre_test(ctx, APPNAME) | |||
os.environ['PATH'] = 'test' + os.pathsep + os.getenv('PATH') | |||
autowaf.run_tests(ctx, APPNAME, ['lilv_test'], dirs=['./src','./test']) | |||
autowaf.post_test(ctx, APPNAME) | |||
def lint(ctx): | |||
subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include,-runtime/sizeof src/* lilv/*', shell=True) |
@@ -0,0 +1,31 @@ | |||
/* | |||
* Carla static lilv code | |||
* Copyright (C) 2012 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the COPYING file | |||
*/ | |||
#include "src/collections.c" | |||
#include "src/instance.c" | |||
#include "src/lib.c" | |||
#include "src/node.c" | |||
#include "src/plugin.c" | |||
#include "src/pluginclass.c" | |||
#include "src/port.c" | |||
#include "src/query.c" | |||
#include "src/scalepoint.c" | |||
#include "src/state.c" | |||
#include "src/ui.c" | |||
#include "src/util.c" | |||
#include "src/world.c" | |||
#include "src/zix/tree.c" |
@@ -0,0 +1 @@ | |||
David Robillard <d@drobilla.net> |
@@ -0,0 +1,13 @@ | |||
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. |
@@ -0,0 +1,59 @@ | |||
Installation Instructions | |||
========================= | |||
Basic Installation | |||
------------------ | |||
Building this software requires only Python. To install with default options: | |||
./waf configure | |||
./waf | |||
./waf install | |||
You may need to become root for the install stage, for example: | |||
sudo ./waf install | |||
Configuration Options | |||
--------------------- | |||
All supported options can be viewed using the command: | |||
./waf --help | |||
Most options only need to be passed during the configure stage, for example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install | |||
Compiler Configuration | |||
---------------------- | |||
Several standard environment variables can be used to control how compilers are | |||
invoked: | |||
* CC: Path to C compiler | |||
* CFLAGS: C compiler options | |||
* CXX: Path to C++ compiler | |||
* CXXFLAGS: C++ compiler options | |||
* CPPFLAGS: C preprocessor options | |||
* LINKFLAGS: Linker options | |||
Installation Directories | |||
------------------------ | |||
The --prefix option (or the PREFIX environment variable) can be used to change | |||
the prefix which all files are installed under. There are also several options | |||
allowing for more fine-tuned control, see the --help output for details. | |||
Packaging | |||
--------- | |||
Everything can be installed to a specific root directory by passing a --destdir | |||
option to the install stage (or setting the DESTDIR environment variable), | |||
which adds a prefix to all install paths. For example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install --destdir=/tmp/package |
@@ -0,0 +1,98 @@ | |||
serd (0.18.2) stable; | |||
* Fix crash when serd_node_new_decimal is called with infinity or NaN | |||
* Fix crash when resolving against non-standard base URIs | |||
* Fix bug that caused "a" abbreviation in non-predicate position | |||
* Disable timestamps in HTML documentation for reproducible build | |||
* Fix clashing symbol "error" in amalgamation build | |||
* Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory) | |||
-- David Robillard <d@drobilla.net> Sat, 22 Dec 2012 21:32:15 -0500 | |||
serd (0.18.0) stable; | |||
* Support digits at start of local names as per new Turtle grammar | |||
* Add incremental read interface suitable for reading from infinite streams | |||
* Add -e option to serdi to use incremental reading | |||
* Add error callback to reader and writer for custom error reporting | |||
* Add -q option to serdi to suppress all non-data output, e.g. errors | |||
* Reset indent when finishing a write | |||
* Report write size correctly when invalid UTF-8 is encountered and a | |||
replacement character is written | |||
* Strip down API documentation to a single clean page | |||
* Fix various hyper-strict warnings | |||
* Do not require a C++ compiler to build | |||
* Add option to build utilities as static binaries | |||
* Upgrade to waf 1.7.2 | |||
-- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 00:18:34 -0400 | |||
serd (0.14.0) stable; | |||
* Use path variables in pkgconfig files | |||
* Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||
* Tolerate invalid characters in string literals by replacing with the | |||
Unicode replacement character | |||
* Report reason for failure to open file in serdi | |||
* Improve write performance by doing bulk writes for unescaped substrings | |||
* Add SerdBulkSink for writing bulk output and corresponding serdi -B option | |||
* Add serdi -f option to prevent URI qualification | |||
* Remove use of multi-byte peek (readahead) and use exactly 1 page for | |||
read buffer (instead of 2) | |||
* Handle a quote as the last character of a long string literal in the | |||
writer (by escaping it) rather than the reader, to avoid writing Turtle | |||
other tools fail to parse. | |||
* Add serd_strtod(), serd_node_new_decimal(), and serd_node_new_integer() | |||
for locale-independent numeric node parsing/serialising. | |||
* Add serd_file_sink for easy writing to a FILE* stream. | |||
* Add serd_chunk_sink for easy writing to a string. | |||
* Escape ASCII control characters in output (e.g. fix problems with string | |||
literals that start with a backspace) | |||
* Improve URI resolution to cover most of the abnormal cases from RFC3986 | |||
* Support file://localhost/foo URIs in serd_uri_to_path() | |||
* Support Windows file://c:/foo URIs in serd_uri_to_path() on all platforms | |||
* Add serd_node_new_blob and serd_base64_decode for handling arbitrary | |||
binary data via base64 encoding. | |||
* Support compilation as C++ under MSVC++. | |||
* Implement pretty-printing for collections. | |||
* Parse collections iteratively in O(1) space. | |||
* Report read error if both "genid" and "docid" IDs are found in the same | |||
document, to prevent silent merging of distinct blank nodes. | |||
* Handle files and strings that start with a UTF-8 Byte Order Mark. | |||
* Add serd_writer_get_env(). | |||
* Add serd_node_new_file_uri() and serd_file_uri_parse() and implement | |||
proper URI to/from path hex escaping, etc. | |||
* Add serd_uri_serialise_relative() for making URIs relative to a base | |||
where possible (by chopping a common prefix and adding dot segments). | |||
* Make URIs serialised by the writer properly escape characters. | |||
* Add serd_writer_set_root_uri() and corresponding -r option to serdi to | |||
enable writing URIs with up references (../). | |||
* Resolve dot segments in serd_uri_resolve() instead of at write time. | |||
* Add serd_reader_set_default_graph() for reading a file as a named graph. | |||
-- David Robillard <d@drobilla.net> Tue, 17 Apr 2012 18:23:53 -0400 | |||
serd (0.5.0) stable; | |||
* Fix pretty printing of successive blank descriptions, i.e. "] , [" | |||
* Avoid writing illegal Turtle names as a result of URI qualifying | |||
* Gracefully handle NULL reader sinks | |||
* Add serd_strerror | |||
* Add serd_env_set_prefix_from_strings for convenience | |||
* Fix erroneously equal SERD_ERR_BAD_SYNTAX and SERD_ERR_BAD_ARG | |||
* Add ability to build static library | |||
-- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||
serd (0.4.2) stable; | |||
* Fix compilation issues on some systems | |||
* Fix build system Python 3 compatibility | |||
-- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||
serd (0.4.0) stable; | |||
* Initial release | |||
-- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 |
@@ -0,0 +1,29 @@ | |||
This library is designed to allow parallel installation of different major | |||
versions. To facilitate this, the shared library name, include directory, and | |||
pkg-config file are suffixed with the major version number of the library. | |||
For example, if this library was named "foo" and at version 1.x.y: | |||
/usr/include/foo-1/foo/foo.h | |||
/usr/lib/foo-1.so.1.x.y | |||
/usr/lib/pkgconfig/foo-1.pc | |||
Dependencies check for pkg-config name "foo-1" and will build | |||
against a compatible version 1, regardless any other installed versions. | |||
*** IMPORTANT GUIDELINES FOR PACKAGERS *** | |||
Packages should follow the same conventions as above, i.e. include the major | |||
version (and only the major version) in the name of the package. Continuing the | |||
example above, the package(s) would be named foo-1 and foo-1-dev. This way, | |||
if/when version 2 comes out, it may be installed at the same time as version 1 | |||
without breaking anything. | |||
Please do not create packages of this library that do not follow these | |||
guidelines, you will break things and cause unnecessary headaches. Please do | |||
not use any number as a suffix other than the actual major version number of the | |||
upstream source package. | |||
Because program and documentation names are not versioned, these should be | |||
included in separate packages which may replace previous versions, since | |||
there is little use in having parallel installations of them. |
@@ -0,0 +1,10 @@ | |||
Serd | |||
---- | |||
Serd is a lightweight C library for RDF syntax which supports reading and | |||
writing Turtle and NTriples. | |||
For more information, see <http://drobilla.net/software/serd>. | |||
-- David Robillard <d@drobilla.net> | |||
@@ -0,0 +1,187 @@ | |||
<doxygenlayout version="1.0"> | |||
<!-- Navigation index tabs for HTML output --> | |||
<navindex> | |||
<tab type="mainpage" visible="yes" title=""/> | |||
<tab type="pages" visible="yes" title="" intro=""/> | |||
<tab type="modules" visible="yes" title="" intro=""/> | |||
<tab type="namespaces" visible="yes" title=""> | |||
<tab type="namespacelist" visible="yes" title="" intro=""/> | |||
<tab type="namespacemembers" visible="yes" title="" intro=""/> | |||
</tab> | |||
<tab type="classes" visible="yes" title=""> | |||
<tab type="classlist" visible="yes" title="" intro=""/> | |||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/> | |||
<tab type="hierarchy" visible="yes" title="" intro=""/> | |||
<tab type="classmembers" visible="yes" title="" intro=""/> | |||
</tab> | |||
<tab type="files" visible="yes" title=""> | |||
<tab type="filelist" visible="yes" title="" intro=""/> | |||
<tab type="globals" visible="yes" title="" intro=""/> | |||
</tab> | |||
<tab type="examples" visible="yes" title="" intro=""/> | |||
</navindex> | |||
<!-- Layout definition for a class page --> | |||
<class> | |||
<briefdescription visible="yes"/> | |||
<includes visible="$SHOW_INCLUDE_FILES"/> | |||
<inheritancegraph visible="$CLASS_GRAPH"/> | |||
<collaborationgraph visible="$COLLABORATION_GRAPH"/> | |||
<allmemberslink visible="yes"/> | |||
<memberdecl> | |||
<nestedclasses visible="yes" title=""/> | |||
<publictypes title=""/> | |||
<publicslots title=""/> | |||
<signals title=""/> | |||
<publicmethods title=""/> | |||
<publicstaticmethods title=""/> | |||
<publicattributes title=""/> | |||
<publicstaticattributes title=""/> | |||
<protectedtypes title=""/> | |||
<protectedslots title=""/> | |||
<protectedmethods title=""/> | |||
<protectedstaticmethods title=""/> | |||
<protectedattributes title=""/> | |||
<protectedstaticattributes title=""/> | |||
<packagetypes title=""/> | |||
<packagemethods title=""/> | |||
<packagestaticmethods title=""/> | |||
<packageattributes title=""/> | |||
<packagestaticattributes title=""/> | |||
<properties title=""/> | |||
<events title=""/> | |||
<privatetypes title=""/> | |||
<privateslots title=""/> | |||
<privatemethods title=""/> | |||
<privatestaticmethods title=""/> | |||
<privateattributes title=""/> | |||
<privatestaticattributes title=""/> | |||
<friends title=""/> | |||
<related title="" subtitle=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
<memberdef> | |||
<inlineclasses title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<constructors title=""/> | |||
<functions title=""/> | |||
<related title=""/> | |||
<variables title=""/> | |||
<properties title=""/> | |||
<events title=""/> | |||
</memberdef> | |||
<usedfiles visible="$SHOW_USED_FILES"/> | |||
<authorsection visible="yes"/> | |||
</class> | |||
<!-- Layout definition for a namespace page --> | |||
<namespace> | |||
<briefdescription visible="yes"/> | |||
<memberdecl> | |||
<nestednamespaces visible="yes" title=""/> | |||
<classes visible="yes" title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
<memberdef> | |||
<inlineclasses title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
</memberdef> | |||
<authorsection visible="yes"/> | |||
</namespace> | |||
<!-- Layout definition for a file page --> | |||
<file> | |||
<briefdescription visible="yes"/> | |||
<includes visible="$SHOW_INCLUDE_FILES"/> | |||
<includegraph visible="$INCLUDE_GRAPH"/> | |||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/> | |||
<sourcelink visible="yes"/> | |||
<memberdecl> | |||
<classes visible="yes" title=""/> | |||
<namespaces visible="yes" title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
<memberdef> | |||
<inlineclasses title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
</memberdef> | |||
<authorsection/> | |||
</file> | |||
<!-- Layout definition for a group page --> | |||
<group> | |||
<briefdescription visible="no"/> | |||
<groupgraph visible="$GROUP_GRAPHS"/> | |||
<detaileddescription title=""/> | |||
<memberdecl> | |||
<nestedgroups visible="yes" title=""/> | |||
<dirs visible="yes" title=""/> | |||
<files visible="yes" title=""/> | |||
<namespaces visible="yes" title=""/> | |||
<classes visible="yes" title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<enumvalues title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<signals title=""/> | |||
<publicslots title=""/> | |||
<protectedslots title=""/> | |||
<privateslots title=""/> | |||
<events title=""/> | |||
<properties title=""/> | |||
<friends title=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<memberdef> | |||
<pagedocs/> | |||
<inlineclasses title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<enumvalues title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<signals title=""/> | |||
<publicslots title=""/> | |||
<protectedslots title=""/> | |||
<privateslots title=""/> | |||
<events title=""/> | |||
<properties title=""/> | |||
<friends title=""/> | |||
</memberdef> | |||
<authorsection visible="yes"/> | |||
</group> | |||
<!-- Layout definition for a directory page --> | |||
<directory> | |||
<briefdescription visible="yes"/> | |||
<directorygraph visible="yes"/> | |||
<memberdecl> | |||
<dirs visible="yes"/> | |||
<files visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
</directory> | |||
</doxygenlayout> |
@@ -0,0 +1,72 @@ | |||
.TH SERDI 1 "08 May 2012" | |||
.SH NAME | |||
.B serdi \- Read and write RDF syntax | |||
.SH SYNOPSIS | |||
serdi [OPTION]... INPUT BASE_URI | |||
.SH OPTIONS | |||
.TP | |||
\fB\-b\fR | |||
Fast bulk output for large serialisations. | |||
.TP | |||
\fB\-c PREFIX\fR | |||
Chop PREFIX from matching blank node IDs. | |||
.TP | |||
\fB\-e\fR | |||
Eat input one character at a time, rather than a page at a time which is the | |||
default. This is useful when reading from a pipe since output will be | |||
generated immediately as input arrives, rather than waiting until an entire | |||
page of input has arrived. With this option serdi uses one page less memory, | |||
but will likely be significantly slower. | |||
.TP | |||
\fB\-f\fR | |||
Keep full URIs in input (don't qualify). | |||
.TP | |||
\fB\-h\fR | |||
Print the command line options. | |||
.TP | |||
\fB\-i SYNTAX\fR | |||
Read input in SYNTAX (`turtle' or `ntriples'). | |||
.TP | |||
\fB\-o SYNTAX\fR | |||
Write output in SYNTAX (`turtle' or `ntriples'). | |||
.TP | |||
\fB\-p PREFIX\fR | |||
Add PREFIX to blank node IDs. | |||
.TP | |||
\fB\-r ROOT_URI\fR | |||
Keep relative URIs within ROOT_URI. | |||
.TP | |||
\fB\-s INPUT\fR | |||
Parse INPUT as a string (terminates options). | |||
.TP | |||
\fB\-v\fR | |||
Display version information and exit. | |||
.SH AUTHOR | |||
Serdi was written by David Robillard <d@drobilla.net> | |||
.SH COPYRIGHT | |||
Copyright \(co 2011-2012 David Robillard. | |||
.br | |||
License: <http://www.opensource.org/licenses/isc> | |||
.br | |||
This is free software; you are free to change and redistribute it. | |||
.br | |||
There is NO WARRANTY, to the extent permitted by law. | |||
.SH "SEE ALSO" | |||
<http://drobilla.net/software/serd> |
@@ -0,0 +1,563 @@ | |||
body { | |||
font-size: medium; | |||
font-family: sans-serif; | |||
} | |||
#top { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
padding: 0; | |||
border-bottom: 1px solid #DDD; | |||
margin-bottom: 1ex; | |||
font-size: xx-large; | |||
font-weight: bold; | |||
} | |||
div.header { | |||
display: none; | |||
} | |||
.tabs { | |||
display: none; | |||
} | |||
h1 h2 h3 h4 h5 h6 { | |||
font-weight: bold; | |||
} | |||
h1 { | |||
font-size: 164%; | |||
} | |||
h2 { | |||
font-size: 132%; | |||
} | |||
h3 { | |||
font-size: 124%; | |||
} | |||
h4 { | |||
font-size: 116%; | |||
} | |||
h5 { | |||
font-size: 108%; | |||
} | |||
h6 { | |||
font-size: 100%; | |||
} | |||
p { | |||
margin: 0 0 1ex 0; | |||
} | |||
br { | |||
display: none; | |||
} | |||
dt { | |||
font-weight: 700; | |||
} | |||
div.multicol { | |||
} | |||
p.startli,p.startdd,p.starttd { | |||
margin-top: 2px; | |||
} | |||
p.endli { | |||
margin-bottom: 0; | |||
} | |||
p.enddd { | |||
margin-bottom: 4px; | |||
} | |||
p.endtd { | |||
margin-bottom: 2px; | |||
} | |||
caption { | |||
font-weight: 700; | |||
} | |||
span.legend { | |||
font-size: 70%; | |||
text-align: center; | |||
} | |||
h3.version { | |||
font-size: 90%; | |||
text-align: center; | |||
} | |||
div.qindex,div.navtab { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
padding: 2px; | |||
} | |||
div.qindex,div.navpath { | |||
width: 100%; | |||
line-height: 140%; | |||
} | |||
div.navtab { | |||
margin-right: 15px; | |||
} | |||
/* @group Link Styling */ | |||
a { | |||
color: #3D8C57; | |||
text-decoration: none; | |||
} | |||
.contents a:visited { | |||
color: #50755E; | |||
} | |||
a:hover { | |||
text-decoration: underline; | |||
} | |||
a.qindexHL { | |||
background-color: #9CAFD4; | |||
color: #FFF; | |||
border: 1px double #869DCA; | |||
} | |||
a.code { | |||
color: #4665A2; | |||
} | |||
a.codeRef { | |||
color: #4665A2; | |||
} | |||
/* @end */ | |||
dl.el { | |||
margin-left: -1cm; | |||
} | |||
.fragment { | |||
font-family: monospace, fixed; | |||
font-size: 105%; | |||
} | |||
pre.fragment { | |||
border: 1px solid #C4C4C4; | |||
background-color: #F9F9F9; | |||
padding: 4px 6px; | |||
margin: 4px 8px 4px 2px; | |||
overflow: auto; | |||
font-size: 9pt; | |||
line-height: 125%; | |||
} | |||
div.ah { | |||
background-color: #000; | |||
font-weight: 700; | |||
color: #FFF; | |||
margin-bottom: 3px; | |||
margin-top: 3px; | |||
padding: .2em; | |||
border: thin solid #333; | |||
} | |||
div.groupHeader { | |||
margin-left: 16px; | |||
margin-top: 12px; | |||
margin-bottom: 6px; | |||
font-weight: 700; | |||
} | |||
div.groupText { | |||
margin-left: 16px; | |||
font-style: italic; | |||
} | |||
body { | |||
background: #FFF; | |||
color: #000; | |||
margin: 0; | |||
} | |||
div.contents { | |||
margin-top: 10px; | |||
margin-left: 10px; | |||
margin-right: 10px; | |||
} | |||
td.indexkey { | |||
background-color: #EBEFF6; | |||
font-weight: 700; | |||
border: 1px solid #C4CFE5; | |||
margin: 2px 0; | |||
padding: 2px 10px; | |||
} | |||
td.indexvalue { | |||
background-color: #EBEFF6; | |||
border: 1px solid #C4CFE5; | |||
padding: 2px 10px; | |||
margin: 2px 0; | |||
} | |||
tr.memlist { | |||
background-color: #EEF1F7; | |||
} | |||
p.formulaDsp { | |||
text-align: center; | |||
} | |||
img.formulaDsp { | |||
} | |||
img.formulaInl { | |||
vertical-align: middle; | |||
} | |||
div.center { | |||
text-align: center; | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
padding: 0; | |||
} | |||
div.center img { | |||
border: 0; | |||
} | |||
address.footer { | |||
text-align: right; | |||
padding: 0 0.25em 0.25em 0; | |||
} | |||
img.footer { | |||
border: 0; | |||
vertical-align: middle; | |||
} | |||
/* @group Code Colorization */ | |||
span.keyword { | |||
color: green; | |||
} | |||
span.keywordtype { | |||
color: #604020; | |||
} | |||
span.keywordflow { | |||
color: #e08000; | |||
} | |||
span.comment { | |||
color: maroon; | |||
} | |||
span.preprocessor { | |||
color: #806020; | |||
} | |||
span.stringliteral { | |||
color: #002080; | |||
} | |||
span.charliteral { | |||
color: teal; | |||
} | |||
span.vhdldigit { | |||
color: #F0F; | |||
} | |||
span.vhdlkeyword { | |||
color: #700070; | |||
} | |||
span.vhdllogic { | |||
color: red; | |||
} | |||
/* @end */ | |||
td.tiny { | |||
font-size: 75%; | |||
} | |||
.dirtab { | |||
padding: 4px; | |||
border-collapse: collapse; | |||
border: 1px solid #A3B4D7; | |||
} | |||
th.dirtab { | |||
background: #EBEFF6; | |||
font-weight: 700; | |||
} | |||
hr { | |||
height: 0; | |||
border: none; | |||
border-top: 1px solid #DDD; | |||
margin: 2em 0 1em; | |||
} | |||
hr.footer { | |||
height: 1px; | |||
} | |||
/* @group Member Descriptions */ | |||
table.memberdecls { | |||
border-spacing: 0; | |||
font-size: small; | |||
} | |||
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
background-color: #FBFBFB; | |||
margin: 0; | |||
padding: 0.25ex; | |||
} | |||
.mdescLeft,.mdescRight { | |||
color: #555; | |||
} | |||
.memItemLeft,.memItemRight,.memTemplParams { | |||
border-top: 1px solid #DDD; | |||
} | |||
.memItemLeft,.memTemplItemLeft { | |||
white-space: nowrap; | |||
padding-left: 2em; | |||
} | |||
.memTemplParams { | |||
color: #464646; | |||
white-space: nowrap; | |||
} | |||
/* @end */ | |||
/* @group Member Details */ | |||
/* Styles for detailed member documentation */ | |||
.memtemplate { | |||
font-size: 80%; | |||
color: #4665A2; | |||
font-weight: bold; | |||
} | |||
.memnav { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
margin-right: 15px; | |||
padding: 2px; | |||
} | |||
.memitem { | |||
padding: 0; | |||
margin: 1ex 0 2ex 0; | |||
border: 1px solid #CCC; | |||
} | |||
.memname { | |||
white-space: nowrap; | |||
font-weight: bold; | |||
} | |||
.memproto { | |||
border-bottom: 1px solid #DDD; | |||
padding: 0.5ex; | |||
font-weight: bold; | |||
background-color: #F3F3F3; | |||
} | |||
.memdoc { | |||
padding: 1ex; | |||
background-color: #FBFBFB; | |||
border-top-width: 0; | |||
} | |||
.paramkey { | |||
text-align: right; | |||
} | |||
.paramtype { | |||
white-space: nowrap; | |||
} | |||
.paramname { | |||
color: #602020; | |||
white-space: nowrap; | |||
} | |||
.paramname em { | |||
font-style: normal; | |||
} | |||
/* @end */ | |||
/* @group Directory (tree) */ | |||
/* for the tree view */ | |||
.ftvtree { | |||
font-family: sans-serif; | |||
margin: 0; | |||
} | |||
/* these are for tree view when used as main index */ | |||
.directory { | |||
font-size: 9pt; | |||
font-weight: bold; | |||
margin: 5px; | |||
} | |||
.directory h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory > h3 { | |||
margin-top: 0; | |||
} | |||
.directory p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory img { | |||
vertical-align: -30%; | |||
} | |||
/* these are for tree view when not used as main index */ | |||
.directory-alt { | |||
font-size: 100%; | |||
font-weight: bold; | |||
} | |||
.directory-alt h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory-alt > h3 { | |||
margin-top: 0; | |||
} | |||
.directory-alt p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory-alt div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory-alt img { | |||
vertical-align: -30%; | |||
} | |||
/* @end */ | |||
div.dynheader { | |||
margin-top: 8px; | |||
} | |||
address { | |||
font-style: normal; | |||
color: #2A3D61; | |||
} | |||
table.doxtable { | |||
border-collapse: collapse; | |||
margin: 0.5ex; | |||
} | |||
table.doxtable td,table.doxtable th { | |||
border: 1px solid #DDD; | |||
padding: 3px 7px 2px; | |||
} | |||
table.doxtable th { | |||
background-color: #F3F3F3; | |||
color: #000; | |||
padding-bottom: 4px; | |||
padding-top: 5px; | |||
text-align: left; | |||
font-weight: bold; | |||
} | |||
.tabsearch { | |||
top: 0; | |||
left: 10px; | |||
height: 36px; | |||
z-index: 101; | |||
overflow: hidden; | |||
font-size: 13px; | |||
} | |||
.navpath ul { | |||
font-size: 11px; | |||
height: 30px; | |||
line-height: 30px; | |||
color: #8AA0CC; | |||
border: 1px solid #C2CDE4; | |||
overflow: hidden; | |||
margin: 0; | |||
padding: 0; | |||
} | |||
.navpath li { | |||
list-style-type: none; | |||
float: left; | |||
padding-left: 10px; | |||
padding-right: 15px; | |||
color: #364D7C; | |||
} | |||
.navpath a { | |||
height: 32px; | |||
display: block; | |||
text-decoration: none; | |||
outline: none; | |||
} | |||
.navpath a:hover { | |||
color: #6884BD; | |||
} | |||
div.summary { | |||
float: right; | |||
font-size: 8pt; | |||
padding-right: 5px; | |||
width: 50%; | |||
text-align: right; | |||
} | |||
div.summary a { | |||
white-space: nowrap; | |||
} | |||
div.header { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
border-bottom: 1px solid #DDD; | |||
} | |||
div.headertitle { | |||
padding: 5px 5px 5px 10px; | |||
font-size: 180%; | |||
font-weight: bold; | |||
} |
@@ -0,0 +1,10 @@ | |||
prefix=@PREFIX@ | |||
exec_prefix=@EXEC_PREFIX@ | |||
libdir=@LIBDIR@ | |||
includedir=@INCLUDEDIR@ | |||
Name: Serd | |||
Version: @SERD_VERSION@ | |||
Description: Lightweight RDF syntax library | |||
Libs: -L${libdir} -l@LIB_SERD@ | |||
Cflags: -I${includedir}/serd-@SERD_MAJOR_VERSION@ |
@@ -0,0 +1,963 @@ | |||
/* | |||
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. | |||
*/ | |||
/** | |||
@file serd.h API for Serd, a lightweight RDF syntax library. | |||
*/ | |||
#ifndef SERD_SERD_H | |||
#define SERD_SERD_H | |||
#include <stdarg.h> | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <stdio.h> | |||
#ifdef SERD_SHARED | |||
# ifdef _WIN32 | |||
# define SERD_LIB_IMPORT __declspec(dllimport) | |||
# define SERD_LIB_EXPORT __declspec(dllexport) | |||
# else | |||
# define SERD_LIB_IMPORT __attribute__((visibility("default"))) | |||
# define SERD_LIB_EXPORT __attribute__((visibility("default"))) | |||
# endif | |||
# ifdef SERD_INTERNAL | |||
# define SERD_API SERD_LIB_EXPORT | |||
# else | |||
# define SERD_API SERD_LIB_IMPORT | |||
# endif | |||
#else | |||
# define SERD_API | |||
#endif | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
/** | |||
@defgroup serd Serd | |||
A lightweight RDF syntax library. | |||
@{ | |||
*/ | |||
/** | |||
Environment. | |||
Represents the state required to resolve a CURIE or relative URI, e.g. the | |||
base URI and set of namespace prefixes at a particular point. | |||
*/ | |||
typedef struct SerdEnvImpl SerdEnv; | |||
/** | |||
RDF reader. | |||
Parses RDF by calling user-provided sink functions as input is consumed | |||
(much like an XML SAX parser). | |||
*/ | |||
typedef struct SerdReaderImpl SerdReader; | |||
/** | |||
RDF writer. | |||
Provides a number of functions to allow writing RDF syntax out to some | |||
stream. These functions are deliberately compatible with the sink functions | |||
used by SerdReader, so a reader can be directly connected to a writer to | |||
re-serialise a document with minimal overhead. | |||
*/ | |||
typedef struct SerdWriterImpl SerdWriter; | |||
/** | |||
Return status code. | |||
*/ | |||
typedef enum { | |||
SERD_SUCCESS, /**< No error */ | |||
SERD_FAILURE, /**< Non-fatal failure */ | |||
SERD_ERR_UNKNOWN, /**< Unknown error */ | |||
SERD_ERR_BAD_SYNTAX, /**< Invalid syntax */ | |||
SERD_ERR_BAD_ARG, /**< Invalid argument */ | |||
SERD_ERR_NOT_FOUND, /**< Not found */ | |||
SERD_ERR_ID_CLASH, /**< Encountered clashing blank node IDs */ | |||
SERD_ERR_BAD_CURIE, /**< Invalid CURIE (e.g. prefix does not exist) */ | |||
SERD_ERR_INTERNAL /**< Unexpected internal error (should not happen) */ | |||
} SerdStatus; | |||
/** | |||
RDF syntax type. | |||
*/ | |||
typedef enum { | |||
/** | |||
Turtle - Terse RDF Triple Language (UTF-8). | |||
@see <a href="http://www.w3.org/TeamSubmission/turtle/">Turtle</a> | |||
*/ | |||
SERD_TURTLE = 1, | |||
/** | |||
NTriples - Line-based RDF triples (ASCII). | |||
@see <a href="http://www.w3.org/TR/rdf-testcases#ntriples">NTriples</a> | |||
*/ | |||
SERD_NTRIPLES = 2 | |||
} SerdSyntax; | |||
/** | |||
Flags indication inline abbreviation information for a statement. | |||
*/ | |||
typedef enum { | |||
SERD_EMPTY_S = 1 << 1, /**< Empty blank node subject */ | |||
SERD_EMPTY_O = 1 << 2, /**< Empty blank node object */ | |||
SERD_ANON_S_BEGIN = 1 << 3, /**< Start of anonymous subject */ | |||
SERD_ANON_O_BEGIN = 1 << 4, /**< Start of anonymous object */ | |||
SERD_ANON_CONT = 1 << 5, /**< Continuation of anonymous node */ | |||
SERD_LIST_S_BEGIN = 1 << 6, /**< Start of list subject */ | |||
SERD_LIST_O_BEGIN = 1 << 7, /**< Start of list object */ | |||
SERD_LIST_CONT = 1 << 8 /**< Continuation of list */ | |||
} SerdStatementFlag; | |||
/** | |||
Bitwise OR of SerdNodeFlag values. | |||
*/ | |||
typedef uint32_t SerdStatementFlags; | |||
/** | |||
Type of a syntactic RDF node. | |||
This is more precise than the type of an abstract RDF node. An abstract | |||
node is either a resource, literal, or blank. In syntax there are two ways | |||
to refer to a resource (by URI or CURIE) and two ways to refer to a blank | |||
(by ID or anonymously). Anonymous (inline) blank nodes are expressed using | |||
SerdStatementFlags rather than this type. | |||
*/ | |||
typedef enum { | |||
/** | |||
The type of a nonexistent node. | |||
This type is useful as a sentinel, but is never emitted by the reader. | |||
*/ | |||
SERD_NOTHING = 0, | |||
/** | |||
Literal value. | |||
A literal optionally has either a language, or a datatype (not both). | |||
*/ | |||
SERD_LITERAL = 1, | |||
/** | |||
URI (absolute or relative). | |||
Value is an unquoted URI string, which is either a relative reference | |||
with respect to the current base URI (e.g. "foo/bar"), or an absolute | |||
URI (e.g. "http://example.org/foo"). | |||
@see <a href="http://tools.ietf.org/html/rfc3986">RFC3986</a>. | |||
*/ | |||
SERD_URI = 2, | |||
/** | |||
CURIE, a shortened URI. | |||
Value is an unquoted CURIE string relative to the current environment, | |||
e.g. "rdf:type". | |||
@see <a href="http://www.w3.org/TR/curie">CURIE Syntax 1.0</a> | |||
*/ | |||
SERD_CURIE = 3, | |||
/** | |||
A blank node. | |||
Value is a blank node ID, e.g. "id3", which is meaningful only within | |||
this serialisation. | |||
@see <a href="http://www.w3.org/TeamSubmission/turtle#nodeID">Turtle | |||
<tt>nodeID</tt></a> | |||
*/ | |||
SERD_BLANK = 4, | |||
} SerdType; | |||
/** | |||
Flags indicating certain string properties relevant to serialisation. | |||
*/ | |||
typedef enum { | |||
SERD_HAS_NEWLINE = 1, /**< Contains line breaks ('\\n' or '\\r') */ | |||
SERD_HAS_QUOTE = 1 << 1 /**< Contains quotes ('"') */ | |||
} SerdNodeFlag; | |||
/** | |||
Bitwise OR of SerdNodeFlag values. | |||
*/ | |||
typedef uint32_t SerdNodeFlags; | |||
/** | |||
A syntactic RDF node. | |||
*/ | |||
typedef struct { | |||
const uint8_t* buf; /**< Value string */ | |||
size_t n_bytes; /**< Size in bytes (not including null) */ | |||
size_t n_chars; /**< Length in characters (not including null)*/ | |||
SerdNodeFlags flags; /**< Node flags (e.g. string properties) */ | |||
SerdType type; /**< Node type */ | |||
} SerdNode; | |||
/** | |||
An unterminated string fragment. | |||
*/ | |||
typedef struct { | |||
const uint8_t* buf; /**< Start of chunk */ | |||
size_t len; /**< Length of chunk in bytes */ | |||
} SerdChunk; | |||
/** | |||
An error description. | |||
*/ | |||
typedef struct { | |||
SerdStatus status; /**< Error code */ | |||
const uint8_t* filename; /**< File where error was encountered, or NULL */ | |||
unsigned line; /**< Line where error was encountered, or 0 */ | |||
unsigned col; /**< Column where error was encountered */ | |||
const char* fmt; /**< Message format string (printf style) */ | |||
va_list* args; /**< Arguments for fmt */ | |||
} SerdError; | |||
/** | |||
A parsed URI. | |||
This struct directly refers to chunks in other strings, it does not own any | |||
memory itself. Thus, URIs can be parsed and/or resolved against a base URI | |||
in-place without allocating memory. | |||
*/ | |||
typedef struct { | |||
SerdChunk scheme; /**< Scheme */ | |||
SerdChunk authority; /**< Authority */ | |||
SerdChunk path_base; /**< Path prefix if relative */ | |||
SerdChunk path; /**< Path suffix */ | |||
SerdChunk query; /**< Query */ | |||
SerdChunk fragment; /**< Fragment */ | |||
} SerdURI; | |||
/** | |||
Syntax style options. | |||
The style of the writer output can be controlled by ORing together | |||
values from this enumeration. Note that some options are only supported | |||
for some syntaxes (e.g. NTriples does not support abbreviation and is | |||
always ASCII). | |||
*/ | |||
typedef enum { | |||
SERD_STYLE_ABBREVIATED = 1, /**< Abbreviate triples when possible. */ | |||
SERD_STYLE_ASCII = 1 << 1, /**< Escape all non-ASCII characters. */ | |||
SERD_STYLE_RESOLVED = 1 << 2, /**< Resolve URIs against base URI. */ | |||
SERD_STYLE_CURIED = 1 << 3, /**< Shorten URIs into CURIEs. */ | |||
SERD_STYLE_BULK = 1 << 4 /**< Write output in pages. */ | |||
} SerdStyle; | |||
/** | |||
@name String Utilities | |||
@{ | |||
*/ | |||
/** | |||
Return a string describing a status code. | |||
*/ | |||
SERD_API | |||
const uint8_t* | |||
serd_strerror(SerdStatus status); | |||
/** | |||
Measure a UTF-8 string. | |||
@return Length of @c str in characters (except NULL). | |||
@param str A null-terminated UTF-8 string. | |||
@param n_bytes (Output) Set to the size of @c str in bytes (except NULL). | |||
@param flags (Output) Set to the applicable flags. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags); | |||
/** | |||
Parse a string to a double. | |||
The API of this function is identical to the standard C strtod function, | |||
except this function is locale-independent and always matches the lexical | |||
format used in the Turtle grammar (the decimal point is always "."). | |||
*/ | |||
SERD_API | |||
double | |||
serd_strtod(const char* str, char** endptr); | |||
/** | |||
Decode a base64 string. | |||
This function can be used to deserialise a blob node created with | |||
serd_node_new_blob(). | |||
@param str Base64 string to decode. | |||
@param len The length of @c str. | |||
@param size Set to the size of the returned blob in bytes. | |||
@return A newly allocated blob which must be freed with free(). | |||
*/ | |||
SERD_API | |||
void* | |||
serd_base64_decode(const uint8_t* str, size_t len, size_t* size); | |||
/** | |||
@} | |||
@name URI | |||
@{ | |||
*/ | |||
static const SerdURI SERD_URI_NULL = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; | |||
/** | |||
Return the local path for @c uri, or NULL if @c uri is not a file URI. | |||
Note this (inappropriately named) function only removes the file scheme if | |||
necessary, and returns @c uri unmodified if it is an absolute path. Percent | |||
encoding and other issues are not handled, to properly convert a file URI to | |||
a path, use serd_file_uri_parse(). | |||
*/ | |||
SERD_API | |||
const uint8_t* | |||
serd_uri_to_path(const uint8_t* uri); | |||
/** | |||
Get the unescaped path and hostname from a file URI. | |||
@param uri A file URI. | |||
@param hostname If non-NULL, set to the hostname, if present. | |||
@return The path component of the URI. | |||
Both the returned path and @c hostname (if applicable) are owned by the | |||
caller and must be freed with free(). | |||
*/ | |||
SERD_API | |||
uint8_t* | |||
serd_file_uri_parse(const uint8_t* uri, uint8_t** hostname); | |||
/** | |||
Return true iff @c utf8 starts with a valid URI scheme. | |||
*/ | |||
SERD_API | |||
bool | |||
serd_uri_string_has_scheme(const uint8_t* utf8); | |||
/** | |||
Parse @c utf8, writing result to @c out. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_uri_parse(const uint8_t* utf8, SerdURI* out); | |||
/** | |||
Set @c out to @c uri resolved against @c base. | |||
*/ | |||
SERD_API | |||
void | |||
serd_uri_resolve(const SerdURI* uri, const SerdURI* base, SerdURI* out); | |||
/** | |||
Sink function for raw string output. | |||
*/ | |||
typedef size_t (*SerdSink)(const void* buf, size_t len, void* stream); | |||
/** | |||
Serialise @c uri with a series of calls to @c sink. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_uri_serialise(const SerdURI* uri, SerdSink sink, void* stream); | |||
/** | |||
Serialise @c uri relative to @c base with a series of calls to @c sink. | |||
The @c uri is written as a relative URI iff if it a child of @c base and @c | |||
root. The optional @c root parameter must be a prefix of @c base and can be | |||
used keep up-references ("../") within a certain namespace. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_uri_serialise_relative(const SerdURI* uri, | |||
const SerdURI* base, | |||
const SerdURI* root, | |||
SerdSink sink, | |||
void* stream); | |||
/** | |||
@} | |||
@name Node | |||
@{ | |||
*/ | |||
static const SerdNode SERD_NODE_NULL = { 0, 0, 0, 0, SERD_NOTHING }; | |||
/** | |||
Make a (shallow) node from @c str. | |||
This measures, but does not copy, @c str. No memory is allocated. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_from_string(SerdType type, const uint8_t* str); | |||
/** | |||
Make a deep copy of @c node. | |||
@return a node that the caller must free with @ref serd_node_free. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_copy(const SerdNode* node); | |||
/** | |||
Return true iff @c a is equal to @c b. | |||
*/ | |||
SERD_API | |||
bool | |||
serd_node_equals(const SerdNode* a, const SerdNode* b); | |||
/** | |||
Simple wrapper for serd_node_new_uri to resolve a URI node. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri_from_node(const SerdNode* uri_node, | |||
const SerdURI* base, | |||
SerdURI* out); | |||
/** | |||
Simple wrapper for serd_node_new_uri to resolve a URI string. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri_from_string(const uint8_t* str, | |||
const SerdURI* base, | |||
SerdURI* out); | |||
/** | |||
Create a new file URI node from a file system path and optional hostname. | |||
Backslashes in Windows paths will be converted and '%' will always be | |||
percent encoded. If @c escape is true, all other invalid characters will be | |||
percent encoded as well. | |||
If @c path is relative, @c hostname is ignored. | |||
If @c out is not NULL, it will be set to the parsed URI. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_file_uri(const uint8_t* path, | |||
const uint8_t* hostname, | |||
SerdURI* out, | |||
bool escape); | |||
/** | |||
Create a new node by serialising @c uri into a new string. | |||
@param uri The URI to parse and serialise. | |||
@param base Base URI to resolve @c uri against (or NULL for no resolution). | |||
@param out Set to the parsing of the new URI (i.e. points only to | |||
memory owned by the new returned node). | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out); | |||
/** | |||
Create a new node by serialising @c d into an xsd:decimal string. | |||
The resulting node will always contain a `.', start with a digit, and end | |||
with a digit (i.e. will have a leading and/or trailing `0' if necessary). | |||
It will never be in scientific notation. A maximum of @c frac_digits digits | |||
will be written after the decimal point, but trailing zeros will | |||
automatically be omitted (except one if @c d is a round integer). | |||
Note that about 16 and 8 fractional digits are required to precisely | |||
represent a double and float, respectively. | |||
@param d The value for the new node. | |||
@param frac_digits The maximum number of digits after the decimal place. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_decimal(double d, unsigned frac_digits); | |||
/** | |||
Create a new node by serialising @c i into an xsd:integer string. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_integer(int64_t i); | |||
/** | |||
Create a node by serialising @c buf into an xsd:base64Binary string. | |||
This function can be used to make a serialisable node out of arbitrary | |||
binary data, which can be decoded using serd_base64_decode(). | |||
@param buf Raw binary input data. | |||
@param size Size of @c buf. | |||
@param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_blob(const void* buf, size_t size, bool wrap_lines); | |||
/** | |||
Free any data owned by @c node. | |||
Note that if @c node is itself dynamically allocated (which is not the case | |||
for nodes created internally by serd), it will not be freed. | |||
*/ | |||
SERD_API | |||
void | |||
serd_node_free(SerdNode* node); | |||
/** | |||
@} | |||
@name Event Handlers | |||
@{ | |||
*/ | |||
/** | |||
Sink (callback) for errors. | |||
@param handle Handle for user data. | |||
@param error Error description. | |||
*/ | |||
typedef SerdStatus (*SerdErrorSink)(void* handle, | |||
const SerdError* error); | |||
/** | |||
Sink (callback) for base URI changes. | |||
Called whenever the base URI of the serialisation changes. | |||
*/ | |||
typedef SerdStatus (*SerdBaseSink)(void* handle, | |||
const SerdNode* uri); | |||
/** | |||
Sink (callback) for namespace definitions. | |||
Called whenever a prefix is defined in the serialisation. | |||
*/ | |||
typedef SerdStatus (*SerdPrefixSink)(void* handle, | |||
const SerdNode* name, | |||
const SerdNode* uri); | |||
/** | |||
Sink (callback) for statements. | |||
Called for every RDF statement in the serialisation. | |||
*/ | |||
typedef SerdStatus (*SerdStatementSink)(void* handle, | |||
SerdStatementFlags flags, | |||
const SerdNode* graph, | |||
const SerdNode* subject, | |||
const SerdNode* predicate, | |||
const SerdNode* object, | |||
const SerdNode* object_datatype, | |||
const SerdNode* object_lang); | |||
/** | |||
Sink (callback) for anonymous node end markers. | |||
This is called to indicate that the anonymous node with the given | |||
@c value will no longer be referred to by any future statements | |||
(i.e. the anonymous serialisation of the node is finished). | |||
*/ | |||
typedef SerdStatus (*SerdEndSink)(void* handle, | |||
const SerdNode* node); | |||
/** | |||
@} | |||
@name Environment | |||
@{ | |||
*/ | |||
/** | |||
Create a new environment. | |||
*/ | |||
SERD_API | |||
SerdEnv* | |||
serd_env_new(const SerdNode* base_uri); | |||
/** | |||
Free @c ns. | |||
*/ | |||
SERD_API | |||
void | |||
serd_env_free(SerdEnv* env); | |||
/** | |||
Get the current base URI. | |||
*/ | |||
SERD_API | |||
const SerdNode* | |||
serd_env_get_base_uri(const SerdEnv* env, | |||
SerdURI* out); | |||
/** | |||
Set the current base URI. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_base_uri(SerdEnv* env, | |||
const SerdNode* uri); | |||
/** | |||
Set a namespace prefix. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_prefix(SerdEnv* env, | |||
const SerdNode* name, | |||
const SerdNode* uri); | |||
/** | |||
Set a namespace prefix. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_prefix_from_strings(SerdEnv* env, | |||
const uint8_t* name, | |||
const uint8_t* uri); | |||
/** | |||
Qualify @c uri into a CURIE if possible. | |||
*/ | |||
SERD_API | |||
bool | |||
serd_env_qualify(const SerdEnv* env, | |||
const SerdNode* uri, | |||
SerdNode* prefix, | |||
SerdChunk* suffix); | |||
/** | |||
Expand @c curie. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_expand(const SerdEnv* env, | |||
const SerdNode* curie, | |||
SerdChunk* uri_prefix, | |||
SerdChunk* uri_suffix); | |||
/** | |||
Expand @c node, which must be a CURIE or URI, to a full URI. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_env_expand_node(const SerdEnv* env, | |||
const SerdNode* node); | |||
/** | |||
Call @c func for each prefix defined in @c env. | |||
*/ | |||
SERD_API | |||
void | |||
serd_env_foreach(const SerdEnv* env, | |||
SerdPrefixSink func, | |||
void* handle); | |||
/** | |||
@} | |||
@name Reader | |||
@{ | |||
*/ | |||
/** | |||
Create a new RDF reader. | |||
*/ | |||
SERD_API | |||
SerdReader* | |||
serd_reader_new(SerdSyntax syntax, | |||
void* handle, | |||
void (*free_handle)(void*), | |||
SerdBaseSink base_sink, | |||
SerdPrefixSink prefix_sink, | |||
SerdStatementSink statement_sink, | |||
SerdEndSink end_sink); | |||
/** | |||
Set a function to be called when errors occur during reading. | |||
The @p error_sink will be called with @p handle as its first argument. If | |||
no error function is set, errors are printed to stderr in GCC style. | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_set_error_sink(SerdReader* reader, | |||
SerdErrorSink error_sink, | |||
void* handle); | |||
/** | |||
Return the @c handle passed to @ref serd_reader_new. | |||
*/ | |||
SERD_API | |||
void* | |||
serd_reader_get_handle(const SerdReader* reader); | |||
/** | |||
Set a prefix to be added to all blank node identifiers. | |||
This is useful when multiple files are to be parsed into the same output | |||
(e.g. a store, or other files). Since Serd preserves blank node IDs, this | |||
could cause conflicts where two non-equivalent blank nodes are merged, | |||
resulting in corrupt data. By setting a unique blank node prefix for each | |||
parsed file, this can be avoided, while preserving blank node names. | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_add_blank_prefix(SerdReader* reader, | |||
const uint8_t* prefix); | |||
/** | |||
Set the URI of the default graph. | |||
If this is set, the reader will emit quads with the graph set to the given | |||
node for any statements that are not in a named graph (which is currently | |||
all of them since Serd currently does not support any graph syntaxes). | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_set_default_graph(SerdReader* reader, | |||
const SerdNode* graph); | |||
/** | |||
Read a file at a given @c uri. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_file(SerdReader* reader, | |||
const uint8_t* uri); | |||
/** | |||
Start an incremental read from a file handle. | |||
Iff @p bulk is true, @p file will be read a page at a time. This is more | |||
efficient, but uses a page of memory and means that an entire page of input | |||
must be ready before any callbacks will fire. To react as soon as input | |||
arrives, set @p bulk to false. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_start_stream(SerdReader* me, | |||
FILE* file, | |||
const uint8_t* name, | |||
bool bulk); | |||
/** | |||
Read a single "chunk" of data during an incremental read. | |||
This function will read a single top level description, and return. This | |||
may be a directive, statement, or several statements; essentially it reads | |||
until a '.' is encountered. This is particularly useful for reading | |||
directly from a pipe or socket. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_chunk(SerdReader* me); | |||
/** | |||
Finish an incremental read from a file handle. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_end_stream(SerdReader* me); | |||
/** | |||
Read @c file. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_file_handle(SerdReader* reader, | |||
FILE* file, | |||
const uint8_t* name); | |||
/** | |||
Read @c utf8. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_string(SerdReader* me, const uint8_t* utf8); | |||
/** | |||
Free @c reader. | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_free(SerdReader* reader); | |||
/** | |||
@} | |||
@name Writer | |||
@{ | |||
*/ | |||
/** | |||
Create a new RDF writer. | |||
*/ | |||
SERD_API | |||
SerdWriter* | |||
serd_writer_new(SerdSyntax syntax, | |||
SerdStyle style, | |||
SerdEnv* env, | |||
const SerdURI* base_uri, | |||
SerdSink sink, | |||
void* stream); | |||
/** | |||
Free @c writer. | |||
*/ | |||
SERD_API | |||
void | |||
serd_writer_free(SerdWriter* writer); | |||
/** | |||
Return the env used by @c writer. | |||
*/ | |||
SERD_API | |||
SerdEnv* | |||
serd_writer_get_env(SerdWriter* writer); | |||
/** | |||
A convenience sink function for writing to a FILE*. | |||
This function can be used as a SerdSink when writing to a FILE*. The | |||
@c stream parameter must be a FILE* opened for writing. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_file_sink(const void* buf, size_t len, void* stream); | |||
/** | |||
A convenience sink function for writing to a string. | |||
This function can be used as a SerdSink to write to a SerdChunk which is | |||
resized as necessary with realloc(). The @c stream parameter must point to | |||
an initialized SerdChunk. When the write is finished, the string should be | |||
retrieved with serd_chunk_sink_finish(). | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_chunk_sink(const void* buf, size_t len, void* stream); | |||
/** | |||
Finish a serialisation to a chunk with serd_chunk_sink(). | |||
The returned string is the result of the serialisation, which is NULL | |||
terminated (by this function) and owned by the caller. | |||
*/ | |||
SERD_API | |||
uint8_t* | |||
serd_chunk_sink_finish(SerdChunk* stream); | |||
/** | |||
Set a function to be called when errors occur during writing. | |||
The @p error_sink will be called with @p handle as its first argument. If | |||
no error function is set, errors are printed to stderr. | |||
*/ | |||
SERD_API | |||
void | |||
serd_writer_set_error_sink(SerdWriter* writer, | |||
SerdErrorSink error_sink, | |||
void* handle); | |||
/** | |||
Set a prefix to be removed from matching blank node identifiers. | |||
*/ | |||
SERD_API | |||
void | |||
serd_writer_chop_blank_prefix(SerdWriter* writer, | |||
const uint8_t* prefix); | |||
/** | |||
Set the current output base URI (and emit directive if applicable). | |||
Note this function can be safely casted to SerdBaseSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_set_base_uri(SerdWriter* writer, | |||
const SerdNode* uri); | |||
/** | |||
Set the current root URI. | |||
The root URI should be a prefix of the base URI. The path of the root URI | |||
is the highest path any relative up-reference can refer to. For example, | |||
with root <file:///foo/root> and base <file:///foo/root/base>, | |||
<file:///foo/root> will be written as <../>, but <file:///foo> will be | |||
written non-relatively as <file:///foo>. If the root is not explicitly set, | |||
it defaults to the base URI, so no up-references will be created at all. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_set_root_uri(SerdWriter* writer, | |||
const SerdNode* uri); | |||
/** | |||
Set a namespace prefix (and emit directive if applicable). | |||
Note this function can be safely casted to SerdPrefixSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_set_prefix(SerdWriter* writer, | |||
const SerdNode* name, | |||
const SerdNode* uri); | |||
/** | |||
Write a statement. | |||
Note this function can be safely casted to SerdStatementSink. | |||
*/ | |||
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* object_datatype, | |||
const SerdNode* object_lang); | |||
/** | |||
Mark the end of an anonymous node's description. | |||
Note this function can be safely casted to SerdEndSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_end_anon(SerdWriter* writer, | |||
const SerdNode* node); | |||
/** | |||
Finish a write. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_finish(SerdWriter* writer); | |||
/** | |||
@} | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* SERD_SERD_H */ |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} |
@@ -0,0 +1,20 @@ | |||
These are the tests for the Turtle Terse RDF Triple Language | |||
that must be passed by conformant systems. See | |||
http://www.dajobe.org/2004/01/turtle/ | |||
for the full conformance information. | |||
The format is a set of good tests and bad tests. | |||
Good tests are a pair of files: | |||
abc.ttl abc.out | |||
which are the input Turtle file and the expected output RDF triples, | |||
written in N-Triples. | |||
bad tests are of the form | |||
bad-XX.ttl | |||
which must fail. | |||
The tests should be performed with an assumed base URI | |||
of http://www.w3.org/2001/sw/DataAccess/df1/tests/ | |||
Dave |
@@ -0,0 +1,219 @@ | |||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
<> rdfs:comment """ | |||
UTF-8 encoded sample plain-text file | |||
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |||
Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25 | |||
The ASCII compatible UTF-8 encoding used in this plain-text file | |||
is defined in Unicode, ISO 10646-1, and RFC 2279. | |||
Using Unicode/UTF-8, you can write in emails and source code things such as | |||
Mathematics and sciences: | |||
∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ | |||
⎪⎢⎜│a²+b³ ⎟⎥⎪ | |||
∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ | |||
⎪⎢⎜⎷ c₈ ⎟⎥⎪ | |||
ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ | |||
⎪⎢⎜ ∞ ⎟⎥⎪ | |||
⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ | |||
⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ | |||
2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ | |||
Linguistics and dictionaries: | |||
ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn | |||
Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] | |||
APL: | |||
((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ | |||
Nicer typography in plain text files: | |||
╔══════════════════════════════════════════╗ | |||
║ ║ | |||
║ • ‘single’ and “double” quotes ║ | |||
║ ║ | |||
║ • Curly apostrophes: “We’ve been here” ║ | |||
║ ║ | |||
║ • Latin-1 apostrophe and accents: '´` ║ | |||
║ ║ | |||
║ • ‚deutsche‘ „Anführungszeichen“ ║ | |||
║ ║ | |||
║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ | |||
║ ║ | |||
║ • ASCII safety test: 1lI|, 0OD, 8B ║ | |||
║ ╭─────────╮ ║ | |||
║ • the euro symbol: │ 14.95 € │ ║ | |||
║ ╰─────────╯ ║ | |||
╚══════════════════════════════════════════╝ | |||
Combining characters: | |||
STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ | |||
Greek (in Polytonic): | |||
The Greek anthem: | |||
Σὲ γνωρίζω ἀπὸ τὴν κόψη | |||
τοῦ σπαθιοῦ τὴν τρομερή, | |||
σὲ γνωρίζω ἀπὸ τὴν ὄψη | |||
ποὺ μὲ βία μετράει τὴ γῆ. | |||
᾿Απ᾿ τὰ κόκκαλα βγαλμένη | |||
τῶν ῾Ελλήνων τὰ ἱερά | |||
καὶ σὰν πρῶτα ἀνδρειωμένη | |||
χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! | |||
From a speech of Demosthenes in the 4th century BC: | |||
Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, | |||
ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς | |||
λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ | |||
τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ | |||
εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ | |||
πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν | |||
οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, | |||
οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν | |||
ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον | |||
τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι | |||
γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν | |||
προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους | |||
σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ | |||
τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ | |||
τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς | |||
τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. | |||
Δημοσθένους, Γ´ ᾿Ολυνθιακὸς | |||
Georgian: | |||
From a Unicode conference invitation: | |||
გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო | |||
კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, | |||
ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს | |||
ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, | |||
ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება | |||
ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, | |||
ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. | |||
Russian: | |||
From a Unicode conference invitation: | |||
Зарегистрируйтесь сейчас на Десятую Международную Конференцию по | |||
Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. | |||
Конференция соберет широкий круг экспертов по вопросам глобального | |||
Интернета и Unicode, локализации и интернационализации, воплощению и | |||
применению Unicode в различных операционных системах и программных | |||
приложениях, шрифтах, верстке и многоязычных компьютерных системах. | |||
Thai (UCS Level 2): | |||
Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese | |||
classic 'San Gua'): | |||
[----------------------------|------------------------] | |||
๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ | |||
สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา | |||
ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา | |||
โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ | |||
เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ | |||
ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ | |||
พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ | |||
ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ | |||
(The above is a two-column text. If combining characters are handled | |||
correctly, the lines of the second column should be aligned with the | |||
| character above.) | |||
Ethiopian: | |||
Proverbs in the Amharic language: | |||
ሰማይ አይታረስ ንጉሥ አይከሰስ። | |||
ብላ ካለኝ እንደአባቴ በቆመጠኝ። | |||
ጌጥ ያለቤቱ ቁምጥና ነው። | |||
ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። | |||
የአፍ ወለምታ በቅቤ አይታሽም። | |||
አይጥ በበላ ዳዋ ተመታ። | |||
ሲተረጉሙ ይደረግሙ። | |||
ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። | |||
ድር ቢያብር አንበሳ ያስር። | |||
ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። | |||
እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። | |||
የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። | |||
ሥራ ከመፍታት ልጄን ላፋታት። | |||
ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። | |||
የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። | |||
ተንጋሎ ቢተፉ ተመልሶ ባፉ። | |||
ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። | |||
እግርህን በፍራሽህ ልክ ዘርጋ። | |||
Runes: | |||
ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ | |||
(Old English, which transcribed into Latin reads 'He cwaeth that he | |||
bude thaem lande northweardum with tha Westsae.' and means 'He said | |||
that he lived in the northern land near the Western Sea.') | |||
Braille: | |||
⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ | |||
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ | |||
⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ | |||
⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ | |||
⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ | |||
⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ | |||
⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ | |||
⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ | |||
⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ | |||
⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ | |||
⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ | |||
⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ | |||
⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ | |||
⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ | |||
⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ | |||
⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ | |||
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ | |||
(The first couple of paragraphs of "A Christmas Carol" by Dickens) | |||
Compact font selection example text: | |||
ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 | |||
abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ | |||
–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд | |||
∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა | |||
Greetings in various languages: | |||
Hello world, Καλημέρα κόσμε, コンニチハ | |||
Box drawing alignment tests: █ | |||
▉ | |||
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ | |||
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ | |||
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ | |||
╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ | |||
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ | |||
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ | |||
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ | |||
▝▀▘▙▄▟ | |||
""" . | |||
<> rdfs:comment """ | |||
Two byte Unicode escape: \u00E0 | |||
Largest Unicode escape in Turtle: \U0010FFFF | |||
""" . |
@@ -0,0 +1,2 @@ | |||
# prefix name must end in a : | |||
@prefix a <#> . |
@@ -0,0 +1,3 @@ | |||
# Forbidden by RDF - predicate cannot be blank | |||
@prefix : <http://example.org/base#> . | |||
:a [ :b :c ] :d . |
@@ -0,0 +1,3 @@ | |||
# Forbidden by RDF - predicate cannot be blank | |||
@prefix : <http://example.org/base#> . | |||
:a [] :b . |
@@ -0,0 +1,3 @@ | |||
# 'a' only allowed as a predicate | |||
@prefix : <http://example.org/base#> . | |||
a :a :b . |
@@ -0,0 +1,3 @@ | |||
# No comma is allowed in collections | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
:a :b ( "apple", "banana" ) . |
@@ -0,0 +1,4 @@ | |||
# N3 {}s are not in Turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
{ :a :b :c . } :d :e . | |||
@@ -0,0 +1,3 @@ | |||
# is and of are not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
:a is :b of :c . |
@@ -0,0 +1,4 @@ | |||
# paths are not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
:a.:b.:c . | |||
:a^:b^:c . |
@@ -0,0 +1,2 @@ | |||
@keywords something. | |||
# @keywords is not in turtle |
@@ -0,0 +1,3 @@ | |||
# implies is not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
:a => :b . |
@@ -0,0 +1,3 @@ | |||
# equivalence is not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
:a = :b . |
@@ -0,0 +1,3 @@ | |||
# @forAll is not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
@forAll :x . |
@@ -0,0 +1,3 @@ | |||
# @forSome is not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
@forSome :x . |
@@ -0,0 +1,3 @@ | |||
# <= is not in turtle | |||
@prefix : <http://example.org/stuff/1.0/> . | |||
:a <= :b . |