@@ -104,3 +104,6 @@ c++/carla-native/zynaddsubfx/UI/SUBnoteUI.cc | |||||
c++/carla-native/zynaddsubfx/UI/SUBnoteUI.h | c++/carla-native/zynaddsubfx/UI/SUBnoteUI.h | ||||
c++/carla-native/zynaddsubfx/UI/VirKeyboard.cc | c++/carla-native/zynaddsubfx/UI/VirKeyboard.cc | ||||
c++/carla-native/zynaddsubfx/UI/VirKeyboard.h | c++/carla-native/zynaddsubfx/UI/VirKeyboard.h | ||||
# Other | |||||
libs/jackbridge/jack/ |
@@ -7,5 +7,5 @@ else | |||||
fi | fi | ||||
INSTALL_PREFIX="X-PREFIX-X" | 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 | fi | ||||
INSTALL_PREFIX="X-PREFIX-X" | 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 | # Script to start Carla bridges | ||||
INSTALL_PREFIX="X-PREFIX-X" | INSTALL_PREFIX="X-PREFIX-X" | ||||
CADENCE_PREFIX="$INSTALL_PREFIX"/lib/cadence | |||||
CARLA_PREFIX="$INSTALL_PREFIX"/lib/carla | |||||
# ---------------------------------------------------------------------- | # ---------------------------------------------------------------------- | ||||
# Check for enough arguments | # Check for enough arguments | ||||
@@ -58,17 +58,17 @@ if [ $RUN_ARCH == "win64" ]; then | |||||
fi | 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 | exit | ||||
fi | fi | ||||
# ---------------------------------------------------------------------- | # ---------------------------------------------------------------------- | ||||
# Check for existing arch binary | # 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 | if [ ! -f $CARLA_EXEC ]; then | ||||
echo "$0: Invalid arch (may not be installed)" | 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 . |