@@ -104,3 +104,6 @@ c++/carla-native/zynaddsubfx/UI/SUBnoteUI.cc | |||
c++/carla-native/zynaddsubfx/UI/SUBnoteUI.h | |||
c++/carla-native/zynaddsubfx/UI/VirKeyboard.cc | |||
c++/carla-native/zynaddsubfx/UI/VirKeyboard.h | |||
# Other | |||
libs/jackbridge/jack/ |
@@ -7,5 +7,5 @@ else | |||
fi | |||
INSTALL_PREFIX="X-PREFIX-X" | |||
export PATH="$INSTALL_PREFIX"/lib/cadence:$PATH | |||
exec $PYTHON $INSTALL_PREFIX/share/cadence/src/carla.py --with-libprefix="$INSTALL_PREFIX" "$@" | |||
export PATH="$INSTALL_PREFIX"/lib/carla:$PATH | |||
exec $PYTHON $INSTALL_PREFIX/share/carla/carla.py --with-libprefix="$INSTALL_PREFIX" "$@" |
@@ -7,4 +7,4 @@ else | |||
fi | |||
INSTALL_PREFIX="X-PREFIX-X" | |||
exec $PYTHON $INSTALL_PREFIX/share/cadence/src/carla_control.py "$@" | |||
exec $PYTHON $INSTALL_PREFIX/share/carla/carla_control.py "$@" |
@@ -2,7 +2,7 @@ | |||
# Script to start Carla bridges | |||
INSTALL_PREFIX="X-PREFIX-X" | |||
CADENCE_PREFIX="$INSTALL_PREFIX"/lib/cadence | |||
CARLA_PREFIX="$INSTALL_PREFIX"/lib/carla | |||
# ---------------------------------------------------------------------- | |||
# Check for enough arguments | |||
@@ -58,17 +58,17 @@ if [ $RUN_ARCH == "win64" ]; then | |||
fi | |||
# ---------------------------------------------------------------------- | |||
# Check for existing cadence folder | |||
# Check for existing carla folder | |||
if [ ! -d $CADENCE_PREFIX ]; then | |||
echo "$0: Cadence folder non-existing, is it installed?" | |||
if [ ! -d $CARLA_PREFIX ]; then | |||
echo "$0: Carla folder does not exist, is it installed?" | |||
exit | |||
fi | |||
# ---------------------------------------------------------------------- | |||
# Check for existing arch binary | |||
CARLA_EXEC="$CADENCE_PREFIX/carla-bridge-$RUN_ARCH" | |||
CARLA_EXEC="$CARLA_PREFIX/carla-bridge-$RUN_ARCH" | |||
if [ ! -f $CARLA_EXEC ]; then | |||
echo "$0: Invalid arch (may not be installed)" | |||
@@ -0,0 +1,62 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for carla libs # | |||
# ----------------------------------------- # | |||
# Created by falkTX | |||
# | |||
all: | |||
# -------------------------------------------------------------- | |||
jackbridge-win32.dll: | |||
$(MAKE) -C jackbridge win32 | |||
jackbridge-win64.dll: | |||
$(MAKE) -C jackbridge win64 | |||
jackbridge-win32.dll.so: | |||
$(MAKE) -C jackbridge wine32 | |||
jackbridge-win64.dll.so: | |||
$(MAKE) -C jackbridge wine64 | |||
# -------------------------------------------------------------- | |||
lilv.a: | |||
$(MAKE) -C lilv | |||
lilv_posix32.a: | |||
$(MAKE) -C lilv posix32 | |||
lilv_posix64.a: | |||
$(MAKE) -C lilv posix64 | |||
lilv_win32.a: | |||
$(MAKE) -C lilv win32 | |||
lilv_win64.a: | |||
$(MAKE) -C lilv win64 | |||
# -------------------------------------------------------------- | |||
rtmempool.a: | |||
$(MAKE) -C rtmempool | |||
rtmempool_posix32.a: | |||
$(MAKE) -C rtmempool posix32 | |||
rtmempool_posix64.a: | |||
$(MAKE) -C rtmempool posix64 | |||
rtmempool_win32.a: | |||
$(MAKE) -C rtmempool win32 | |||
rtmempool_win64.a: | |||
$(MAKE) -C rtmempool win64 | |||
# -------------------------------------------------------------- | |||
clean: | |||
rm -f *.a *.def *.dll *.so | |||
$(MAKE) clean -C lilv | |||
$(MAKE) clean -C rtmempool |
@@ -0,0 +1,51 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for jackbridge # | |||
# ------------------------------------- # | |||
# Created by falkTX | |||
# | |||
include ../../Makefile.mk | |||
# -------------------------------------------------------------- | |||
WINECC ?= winegcc | |||
BUILD_C_FLAGS += -DJACKBRIDGE_EXPORT -I. | |||
LINK_FLAGS += -shared | |||
WIN_BUILD_FLAGS = $(BUILD_C_FLAGS) -DJACKBRIDGE_DUMMY -w | |||
WIN_32BIT_FLAGS = $(32BIT_FLAGS) | |||
WIN_64BIT_FLAGS = $(64BIT_FLAGS) | |||
WIN_LINK_FLAGS = $(LINK_FLAGS) | |||
WINE_BUILD_FLAGS = $(BUILD_C_FLAGS) -fPIC | |||
WINE_32BIT_FLAGS = $(32BIT_FLAGS) -L/usr/lib32/wine -L/usr/lib/i386-linux-gnu/wine | |||
WINE_64BIT_FLAGS = $(64BIT_FLAGS) -L/usr/lib64/wine -L/usr/lib/x86_64-linux-gnu/wine | |||
WINE_LINK_FLAGS = $(LINK_FLAGS) $(shell pkg-config --libs jack) -ldl | |||
OBJS = jackbridge.c | |||
# -------------------------------------------------------------- | |||
all: | |||
win32: ../jackbridge-win32.dll | |||
win64: ../jackbridge-win64.dll | |||
wine32: ../jackbridge-win32.dll.so | |||
wine64: ../jackbridge-win64.dll.so | |||
# -------------------------------------------------------------- | |||
../jackbridge-win32.dll: $(OBJS) | |||
$(CC) $^ $(WIN_BUILD_FLAGS) $(WIN_32BIT_FLAGS) $(WIN_LINK_FLAGS) -Wl,--output-def,$@.def,--out-implib,$@.a -o $@ && $(STRIP) $@ | |||
../jackbridge-win64.dll: $(OBJS) | |||
$(CC) $^ $(WIN_BUILD_FLAGS) $(WIN_64BIT_FLAGS) $(WIN_LINK_FLAGS) -Wl,--output-def,$@.def,--out-implib,$@.a -o $@ && $(STRIP) $@ | |||
../jackbridge-win32.dll.so: $(OBJS) ../jackbridge-win32.dll.def | |||
$(WINECC) $^ $(WINE_BUILD_FLAGS) $(WINE_32BIT_FLAGS) $(WINE_LINK_FLAGS) -mno-cygwin -o $@ && $(STRIP) $@ | |||
../jackbridge-win64.dll.so: $(OBJS) ../jackbridge-win64.dll.def | |||
$(WINECC) $^ $(WINE_BUILD_FLAGS) $(WINE_64BIT_FLAGS) $(WINE_LINK_FLAGS) -mno-cygwin -o $@ && $(STRIP) $@ | |||
# -------------------------------------------------------------- |
@@ -0,0 +1,259 @@ | |||
/* | |||
* JackBridge | |||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the COPYING file | |||
*/ | |||
#include "jackbridge.h" | |||
// ----------------------------------------------------------------------------- | |||
jack_client_t* jackbridge_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_client_open(client_name, options, status); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_client_close(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_client_close(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_client_name_size() | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_client_name_size(); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
char* jackbridge_get_client_name(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_get_client_name(client); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_port_name_size() | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_name_size(); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_recompute_total_latencies(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_recompute_total_latencies(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void jackbridge_port_get_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_port_get_latency_range(port, mode, range); | |||
#endif | |||
} | |||
void jackbridge_port_set_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_port_set_latency_range(port, mode, range); | |||
#endif | |||
} | |||
int jackbridge_activate(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_activate(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_deactivate(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_deactivate(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_on_shutdown(client, shutdown_callback, arg); | |||
#endif | |||
} | |||
int jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback latency_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_latency_callback(client, latency_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_process_callback(client, process_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallback freewheel_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_freewheel_callback(client, freewheel_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback bufsize_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_buffer_size_callback(client, bufsize_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback srate_callback, void* arg) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_set_sample_rate_callback(client, srate_callback, arg); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_get_sample_rate(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_get_buffer_size(client); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_register(client, port_name, port_type, flags, buffer_size); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_port_unregister(jack_client_t* client, jack_port_t* port) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_unregister(client, port); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_port_get_buffer(port, nframes); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------------- | |||
uint32_t jackbridge_midi_get_event_count(void* port_buffer) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_get_event_count(port_buffer); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
int jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_event_get(event, port_buffer, event_index); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void jackbridge_midi_clear_buffer(void* port_buffer) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
jack_midi_clear_buffer(port_buffer); | |||
#endif | |||
} | |||
jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_event_reserve(port_buffer, time, data_size); | |||
#else | |||
return NULL; | |||
#endif | |||
} | |||
int jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_midi_event_write(port_buffer, time, data, data_size); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------------- | |||
jack_transport_state_t jackbridge_transport_query(const jack_client_t* client, jack_position_t* pos) | |||
{ | |||
#ifndef JACKBRIDGE_DUMMY | |||
return jack_transport_query(client, pos); | |||
#else | |||
return JackTransportStopped; | |||
#endif | |||
} |
@@ -0,0 +1,111 @@ | |||
/* | |||
* JackBridge | |||
* Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the COPYING file | |||
*/ | |||
#ifndef __JACKBRIDGE_H__ | |||
#define __JACKBRIDGE_H__ | |||
#if defined(_WIN32) && ! defined(__WINE__) | |||
# include <stdint.h> | |||
# include <windows.h> | |||
# define BRIDGE_EXPORT __declspec (dllexport) | |||
#else | |||
# define BRIDGE_EXPORT __attribute__ ((visibility("default"))) | |||
#endif | |||
#ifdef __WINE__ | |||
# define GNU_WIN32 // fix jack_native_thread_t | |||
#endif | |||
#include <jack/jack.h> | |||
#include <jack/midiport.h> | |||
#include <jack/transport.h> | |||
#ifdef JACKBRIDGE_EXPORT | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
BRIDGE_EXPORT jack_client_t* jackbridge_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...); | |||
BRIDGE_EXPORT int jackbridge_client_close(jack_client_t* client); | |||
BRIDGE_EXPORT int jackbridge_client_name_size(); | |||
BRIDGE_EXPORT char* jackbridge_get_client_name(jack_client_t* client); | |||
BRIDGE_EXPORT int jackbridge_port_name_size(); | |||
BRIDGE_EXPORT int jackbridge_recompute_total_latencies(jack_client_t* client); | |||
BRIDGE_EXPORT void jackbridge_port_get_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range); | |||
BRIDGE_EXPORT void jackbridge_port_set_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range); | |||
BRIDGE_EXPORT int jackbridge_activate(jack_client_t* client); | |||
BRIDGE_EXPORT int jackbridge_deactivate(jack_client_t* client); | |||
BRIDGE_EXPORT void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback latency_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallback freewheel_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback bufsize_callback, void* arg); | |||
BRIDGE_EXPORT int jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback srate_callback, void* arg); | |||
BRIDGE_EXPORT jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client); | |||
BRIDGE_EXPORT jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client); | |||
BRIDGE_EXPORT jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); | |||
BRIDGE_EXPORT int jackbridge_port_unregister(jack_client_t* client, jack_port_t* port); | |||
BRIDGE_EXPORT void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes); | |||
BRIDGE_EXPORT uint32_t jackbridge_midi_get_event_count(void* port_buffer); | |||
BRIDGE_EXPORT int jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index); | |||
BRIDGE_EXPORT void jackbridge_midi_clear_buffer(void* port_buffer); | |||
BRIDGE_EXPORT jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size); | |||
BRIDGE_EXPORT int jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size); | |||
BRIDGE_EXPORT jack_transport_state_t jackbridge_transport_query(const jack_client_t* client, jack_position_t* pos); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#else // JACKBRIDGE_EXPORT | |||
#define jackbridge_client_open jack_client_open | |||
#define jackbridge_client_close jack_client_close | |||
#define jackbridge_client_name_size jack_client_name_size | |||
#define jackbridge_get_client_name jack_get_client_name | |||
#define jackbridge_port_name_size jack_port_name_size | |||
#define jackbridge_recompute_total_latencies jack_recompute_total_latencies | |||
#define jackbridge_port_get_latency_range jack_port_get_latency_range | |||
#define jackbridge_port_set_latency_range jack_port_set_latency_range | |||
#define jackbridge_activate jack_activate | |||
#define jackbridge_deactivate jack_deactivate | |||
#define jackbridge_on_shutdown jack_on_shutdown | |||
#define jackbridge_set_latency_callback jack_set_latency_callback | |||
#define jackbridge_set_process_callback jack_set_process_callback | |||
#define jackbridge_set_freewheel_callback jack_set_freewheel_callback | |||
#define jackbridge_set_buffer_size_callback jack_set_buffer_size_callback | |||
#define jackbridge_set_sample_rate_callback jack_set_sample_rate_callback | |||
#define jackbridge_get_sample_rate jack_get_sample_rate | |||
#define jackbridge_get_buffer_size jack_get_buffer_size | |||
#define jackbridge_port_register jack_port_register | |||
#define jackbridge_port_unregister jack_port_unregister | |||
#define jackbridge_port_get_buffer jack_port_get_buffer | |||
#define jackbridge_midi_get_event_count jack_midi_get_event_count | |||
#define jackbridge_midi_event_get jack_midi_event_get | |||
#define jackbridge_midi_clear_buffer jack_midi_clear_buffer | |||
#define jackbridge_midi_event_reserve jack_midi_event_reserve | |||
#define jackbridge_midi_event_write jack_midi_event_write | |||
#define jackbridge_transport_query jack_transport_query | |||
#endif // JACKBRIDGE_EXPORT | |||
#endif // __JACKBRIDGE_H__ |
@@ -0,0 +1,96 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for lilv # | |||
# ----------------------- # | |||
# Created by falkTX | |||
# | |||
include ../../Makefile.mk | |||
# -------------------------------------------------------------- | |||
SERD_VERSION = 0.18.2 | |||
SORD_VERSION = 0.10.4 | |||
SRATOM_VERSION = 0.4.0 | |||
LILV_VERSION = 0.14.4 | |||
BUILD_C_FLAGS += -fvisibility=hidden -fPIC -w | |||
BUILD_C_FLAGS += -Iconfig -I../../includes | |||
OBJS = serd.o sord.o sratom.o lilv.o | |||
OBJS_posix32 = serd_posix32.o sord_posix32.o sratom_posix32.o lilv_posix32.o | |||
OBJS_posix64 = serd_posix64.o sord_posix64.o sratom_posix64.o lilv_posix64.o | |||
OBJS_win32 = serd_win32.o sord_win32.o sratom_win32.o lilv_win32.o | |||
OBJS_win64 = serd_win64.o sord_win64.o sratom_win64.o lilv_win64.o | |||
# -------------------------------------------------------------- | |||
all: ../lilv.a | |||
posix32: ../lilv_posix32.a | |||
posix64: ../lilv_posix64.a | |||
win32: ../lilv_win32.a | |||
win64: ../lilv_win64.a | |||
# -------------------------------------------------------------- | |||
../lilv.a: $(OBJS) | |||
$(AR) rs $@ $^ | |||
../lilv_posix32.a: $(OBJS_posix32) | |||
$(AR) rs $@ $^ | |||
../lilv_posix64.a: $(OBJS_posix64) | |||
$(AR) rs $@ $^ | |||
../lilv_win32.a: $(OBJS_win32) | |||
$(AR) rs $@ $^ | |||
../lilv_win64.a: $(OBJS_win64) | |||
$(AR) rs $@ $^ | |||
# -------------------------------------------------------------- | |||
serd.o: serd.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) -c -o $@ | |||
sord.o: sord.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src -c -o $@ | |||
sratom.o: sratom.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) -c -o $@ | |||
lilv.o: lilv.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src -c -o $@ | |||
# -------------------------------------------------------------- | |||
serd_%32.o: serd.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) $(32BIT_FLAGS) -c -o $@ | |||
sord_%32.o: sord.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src $(32BIT_FLAGS) -c -o $@ | |||
sratom_%32.o: sratom.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) $(32BIT_FLAGS) -c -o $@ | |||
lilv_%32.o: lilv.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src $(32BIT_FLAGS) -c -o $@ | |||
# -------------------------------------------------------------- | |||
serd_%64.o: serd.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) $(64BIT_FLAGS) -c -o $@ | |||
sord_%64.o: sord.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src $(64BIT_FLAGS) -c -o $@ | |||
sratom_%64.o: sratom.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) $(64BIT_FLAGS) -c -o $@ | |||
lilv_%64.o: lilv.c | |||
$(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src $(64BIT_FLAGS) -c -o $@ | |||
# -------------------------------------------------------------- | |||
clean: | |||
rm -f *.o |
@@ -0,0 +1,35 @@ | |||
#ifndef _LILV_CONFIG_H_ | |||
#define _LILV_CONFIG_H_ | |||
#define LILV_VERSION "0.14.4" | |||
#define LILV_NEW_LV2 1 | |||
#define HAVE_LV2 1 | |||
#define HAVE_SERD 1 | |||
#define HAVE_SORD 1 | |||
#define HAVE_SRATOM 1 | |||
#define HAVE_FILENO 1 | |||
#define HAVE_CLOCK_GETTIME 1 | |||
#ifdef __WIN32__ | |||
#define LILV_PATH_SEP ";" | |||
#define LILV_DIR_SEP "\\" | |||
#else | |||
#define LILV_PATH_SEP ":" | |||
#define LILV_DIR_SEP "/" | |||
#define HAVE_FLOCK 1 | |||
#endif | |||
#if defined(__APPLE__) | |||
#define LILV_DEFAULT_LV2_PATH "~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2:/Library/Audio/Plug-Ins/LV2" | |||
#elif defined(__HAIKU__) | |||
#define LILV_DEFAULT_LV2_PATH "~/.lv2:/boot/common/add-ons/lv2" | |||
#elif defined(__WIN32__) | |||
#define LILV_DEFAULT_LV2_PATH "%APPDATA%\\LV2;%COMMONPROGRAMFILES%\\LV2" | |||
#else | |||
#define LILV_DEFAULT_LV2_PATH "~/.lv2:/usr/lib/lv2:/usr/local/lib/lv2" | |||
#endif | |||
#endif /* _LILV_CONFIG_H_ */ |
@@ -0,0 +1,21 @@ | |||
#ifndef _SERD_CONFIG_H_ | |||
#define _SERD_CONFIG_H_ | |||
#define SERD_VERSION "0.18.2" | |||
#if defined(__APPLE__) || defined(__HAIKU__) | |||
#define HAVE_FMAX 1 | |||
#define HAVE_FILENO 1 | |||
#define HAVE_POSIX_MEMALIGN 1 | |||
#elif defined(__WIN32__) | |||
#define HAVE_FMAX 1 | |||
#define HAVE_FILENO 1 | |||
#else | |||
#define HAVE_FMAX 1 | |||
#define HAVE_FILENO 1 | |||
#define HAVE_POSIX_MEMALIGN 1 | |||
#define HAVE_POSIX_FADVISE 1 | |||
#endif | |||
#endif /* _SERD_CONFIG_H_ */ |
@@ -0,0 +1,10 @@ | |||
#ifndef _SORD_CONFIG_H_ | |||
#define _SORD_CONFIG_H_ | |||
#define SORD_VERSION "0.10.4" | |||
#define HAVE_SERD 1 | |||
#define HAVE_PCRE 1 | |||
#endif /* _SORD_CONFIG_H_ */ |
@@ -0,0 +1,11 @@ | |||
#ifndef _SRATOM_CONFIG_H_ | |||
#define _SRATOM_CONFIG_H_ | |||
#define SRATOM_VERSION "0.4.0" | |||
#define HAVE_LV2 1 | |||
#define HAVE_SERD 1 | |||
#define HAVE_SORD 1 | |||
#endif /* _SRATOM_CONFIG_H_ */ |
@@ -0,0 +1,20 @@ | |||
diff -U 3 -H -b -B -d -r -N -- lilv-0.14.4.old/src/util.c lilv-0.14.4/src/util.c | |||
--- lilv-0.14.4.old/src/util.c 2012-08-09 21:51:00.000000000 +0100 | |||
+++ lilv-0.14.4/src/util.c 2012-09-15 01:20:07.908701939 +0100 | |||
@@ -29,7 +29,6 @@ | |||
#include <string.h> | |||
#ifdef _WIN32 | |||
-# define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */ | |||
# include <windows.h> | |||
# include <direct.h> | |||
# include <io.h> | |||
@@ -426,7 +425,7 @@ | |||
int ret = 0; | |||
if (strcmp(oldpath, newpath)) { | |||
#ifdef _WIN32 | |||
- ret = !CreateSymbolicLink(newpath, oldpath, 0); | |||
+ ret = 0; | |||
#else | |||
ret = symlink(oldpath, newpath); | |||
#endif |
@@ -0,0 +1,137 @@ | |||
diff -U 3 -H -d -r -N -- lilv-0.14.4.old/lilv/lilv.h lilv-0.14.4/lilv/lilv.h | |||
--- lilv-0.14.4.old/lilv/lilv.h 2012-09-07 19:00:48.464571333 +0100 | |||
+++ lilv-0.14.4/lilv/lilv.h 2012-09-07 18:54:00.626548936 +0100 | |||
@@ -1693,6 +1693,25 @@ | |||
lilv_ui_get_binary_uri(const LilvUI* ui); | |||
/** | |||
+ Custom calls | |||
+*/ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_supported_features(const LilvUI* ui); | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_required_features(const LilvUI* ui); | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_optional_features(const LilvUI* ui); | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_extension_data(const LilvUI* ui); | |||
+ | |||
+/** | |||
@} | |||
@} | |||
*/ | |||
diff -U 3 -H -d -r -N -- lilv-0.14.4.old/lilv/lilvmm.hpp lilv-0.14.4/lilv/lilvmm.hpp | |||
--- lilv-0.14.4.old/lilv/lilvmm.hpp 2012-07-18 02:42:43.000000000 +0100 | |||
+++ lilv-0.14.4/lilv/lilvmm.hpp 2012-09-07 18:53:14.134318379 +0100 | |||
@@ -136,6 +136,7 @@ | |||
struct Nodes { | |||
LILV_WRAP_COLL(Nodes, Node, nodes); | |||
LILV_WRAP1(bool, nodes, contains, const Node, node); | |||
+ LILV_WRAP0(Node, nodes, get_first); | |||
}; | |||
struct Port { | |||
@@ -167,6 +168,26 @@ | |||
const LilvPort* me; | |||
}; | |||
+struct UI { | |||
+ inline UI(const LilvUI* c_obj) : me(c_obj) {} | |||
+ LILV_WRAP_CONVERSION(const LilvUI); | |||
+ | |||
+ LILV_WRAP0(Node, ui, get_uri); | |||
+ LILV_WRAP1(bool, ui, is_a, LilvNode*, ui_class); | |||
+ LILV_WRAP0(Node, ui, get_bundle_uri); | |||
+ LILV_WRAP0(Node, ui, get_binary_uri); | |||
+ LILV_WRAP0(Nodes, ui, get_supported_features); | |||
+ LILV_WRAP0(Nodes, ui, get_required_features); | |||
+ LILV_WRAP0(Nodes, ui, get_optional_features); | |||
+ LILV_WRAP0(Nodes, ui, get_extension_data); | |||
+ | |||
+ const LilvUI* me; | |||
+}; | |||
+ | |||
+struct UIs { | |||
+ LILV_WRAP_COLL(UIs, UI, uis); | |||
+}; | |||
+ | |||
struct Plugin { | |||
inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvPlugin); | |||
@@ -190,6 +211,8 @@ | |||
LILV_WRAP0(Node, plugin, get_author_email); | |||
LILV_WRAP0(Node, plugin, get_author_homepage); | |||
LILV_WRAP0(bool, plugin, is_replaced); | |||
+ LILV_WRAP0(Nodes, plugin, get_extension_data); | |||
+ LILV_WRAP0(UIs, plugin, get_uis); | |||
inline Port get_port_by_index(unsigned index) { | |||
return Port(me, lilv_plugin_get_port_by_index(me, index)); | |||
diff -U 3 -H -d -r -N -- lilv-0.14.4.old/src/ui.c lilv-0.14.4/src/ui.c | |||
--- lilv-0.14.4.old/src/ui.c 2012-09-07 19:00:48.464571333 +0100 | |||
+++ lilv-0.14.4/src/ui.c 2012-09-07 18:59:51.652289664 +0100 | |||
@@ -128,3 +128,57 @@ | |||
assert(ui->binary_uri); | |||
return ui->binary_uri; | |||
} | |||
+ | |||
+static LilvNodes* | |||
+lilv_ui_get_value_internal(const LilvUI* ui, | |||
+ const SordNode* predicate) | |||
+{ | |||
+ return lilv_world_query_values_internal( | |||
+ ui->world, ui->uri->node, predicate, NULL); | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_supported_features(const LilvUI* ui) | |||
+{ | |||
+ LilvNodes* optional = lilv_ui_get_optional_features(ui); | |||
+ LilvNodes* required = lilv_ui_get_required_features(ui); | |||
+ LilvNodes* result = lilv_nodes_new(); | |||
+ | |||
+ LILV_FOREACH(nodes, i, optional) | |||
+ zix_tree_insert((ZixTree*)result, | |||
+ lilv_node_duplicate(lilv_nodes_get(optional, i)), | |||
+ NULL); | |||
+ LILV_FOREACH(nodes, i, required) | |||
+ zix_tree_insert((ZixTree*)result, | |||
+ lilv_node_duplicate(lilv_nodes_get(required, i)), | |||
+ NULL); | |||
+ | |||
+ lilv_nodes_free(optional); | |||
+ lilv_nodes_free(required); | |||
+ | |||
+ return result; | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_required_features(const LilvUI* ui) | |||
+{ | |||
+ return lilv_ui_get_value_internal( | |||
+ ui, ui->world->uris.lv2_requiredFeature); | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_optional_features(const LilvUI* ui) | |||
+{ | |||
+ return lilv_ui_get_value_internal( | |||
+ ui, ui->world->uris.lv2_optionalFeature); | |||
+} | |||
+ | |||
+LILV_API | |||
+LilvNodes* | |||
+lilv_ui_get_extension_data(const LilvUI* ui) | |||
+{ | |||
+ return lilv_ui_get_value_internal(ui, ui->world->uris.lv2_extensionData); | |||
+} |
@@ -0,0 +1,11 @@ | |||
diff -U 3 -H -b -B -d -r -N -- lilv-0.14.4.old/lilv/lilvmm.hpp lilv-0.14.4/lilv/lilvmm.hpp | |||
--- lilv-0.14.4.old/lilv/lilvmm.hpp 2012-09-13 12:47:55.000000000 +0100 | |||
+++ lilv-0.14.4/lilv/lilvmm.hpp 2012-09-13 12:48:10.950555311 +0100 | |||
@@ -60,6 +60,7 @@ | |||
#endif | |||
struct Node { | |||
+ inline Node(LilvNode* node) : me(node) {} | |||
inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {} | |||
inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {} | |||
@@ -0,0 +1,8 @@ | |||
Author: | |||
David Robillard <d@drobilla.net> | |||
GTK2 GUI and I18N support: | |||
Lars Luthman <lars.luthman@gmail.com> | |||
Dynamic manifest support: | |||
Stefano D'Angelo |
@@ -0,0 +1,13 @@ | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
@@ -0,0 +1,59 @@ | |||
Installation Instructions | |||
========================= | |||
Basic Installation | |||
------------------ | |||
Building this software requires only Python. To install with default options: | |||
./waf configure | |||
./waf | |||
./waf install | |||
You may need to become root for the install stage, for example: | |||
sudo ./waf install | |||
Configuration Options | |||
--------------------- | |||
All supported options can be viewed using the command: | |||
./waf --help | |||
Most options only need to be passed during the configure stage, for example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install | |||
Compiler Configuration | |||
---------------------- | |||
Several standard environment variables can be used to control how compilers are | |||
invoked: | |||
* CC: Path to C compiler | |||
* CFLAGS: C compiler options | |||
* CXX: Path to C++ compiler | |||
* CXXFLAGS: C++ compiler options | |||
* CPPFLAGS: C preprocessor options | |||
* LINKFLAGS: Linker options | |||
Installation Directories | |||
------------------------ | |||
The --prefix option (or the PREFIX environment variable) can be used to change | |||
the prefix which all files are installed under. There are also several options | |||
allowing for more fine-tuned control, see the --help output for details. | |||
Packaging | |||
--------- | |||
Everything can be installed to a specific root directory by passing a --destdir | |||
option to the install stage (or setting the DESTDIR environment variable), | |||
which adds a prefix to all install paths. For example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install --destdir=/tmp/package |
@@ -0,0 +1,90 @@ | |||
lilv (0.14.4) stable; | |||
* Deprecate old flawed Lilv::Instance constructors | |||
* Fix documentation for ui_type parameter of lilv_ui_is_supported() | |||
* Fix crash when lv2info is run with an invalid URI argument | |||
* Gracefully handle failure to save plugin state and print error message | |||
* Reduce memory usage (per node) | |||
* Simpler node implementation always backed by a SordNode | |||
* Make all 'zix' symbols private to avoid symbol clashes in static builds | |||
* Add lv2bench utility | |||
* Fix various hyper-strict warnings | |||
* Do not require a C++ compiler to build | |||
* Add option to build utilities as static binaries | |||
* Upgrade to waf 1.7.2 | |||
* lilvmm.hpp: Make Lilv::Instance handle features and failed instantiations | |||
* lilvmm.hpp: Add Lilv::Instance::get_handle() | |||
* lilvmm.hpp: Add Lilv::Instance::get_extension_data() | |||
-- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 01:38:29 -0400 | |||
lilv (0.14.2) stable; | |||
* Fix dynmanifest support | |||
-- David Robillard <d@drobilla.net> Thu, 19 Apr 2012 16:11:31 -0400 | |||
lilv (0.14.0) stable; | |||
* Add lilv_plugin_get_extension_data | |||
* Use path variables in pkgconfig files | |||
* Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||
* Make Lilv::uri_to_path static inline (fix linking errors) | |||
* Use correct URI for dcterms:replaces (for hiding old plugins): | |||
"http://purl.org/dc/terms/replaces" | |||
* Fix compilation on BSD | |||
* Only load dynmanifest libraries once per bundle, not once per plugin | |||
* Fix lilv_world_find_nodes to work with wildcard subjects | |||
* Add lilv_plugin_get_related to get resources related to plugins that | |||
are not directly rdfs:seeAlso linked (e.g. presets) | |||
* Add lilv_world_load_resource for related resources (e.g. presets) | |||
* Print presets in lv2info | |||
* Remove locale smashing kludges and use new serd functions for converting | |||
nodes to/from numbers. | |||
* Add LilvState API for handling plugin state. This makes it simple to | |||
save and restore plugin state both in memory and on disk, as well as | |||
save presets in a host-sharable way since the disk format is identical | |||
to the LV2 presets format. | |||
* Update old references to lv2_list (now lv2ls) | |||
* Support compilation as C++ under MSVC++. | |||
* Remove use of wordexp. | |||
* Add lilv_plugin_get_port_by_designation() and lilv_port_get_index() as an | |||
improved generic alternative to lilv_plugin_get_latency_port_index(). | |||
* Add lilv_plugin_get_project() and get author information from project if | |||
it is not given directly on the plugin. | |||
-- David Robillard <d@drobilla.net> Wed, 18 Apr 2012 20:06:28 -0400 | |||
lilv (0.5.0) stable; | |||
* Remove glib dependency | |||
* Add lv2core as a pkg-config dependency (for lv2.h header include) | |||
* Obey prefix when installing bash completion script | |||
* Support integer minimum, maximum, and default port values in | |||
lilv_plugin_get_port_ranges_float | |||
* Add ability to build static library | |||
-- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||
lilv (0.4.4) stable; | |||
* Fix building python bindings | |||
* Make free methods tolerate being called on NULL | |||
* Remove lv2jack (replaced by jalv) | |||
* Fix parsing extra plugin data files in other bundles | |||
* Fix lilv_ui_is_supported when Lilv is built independently | |||
-- David Robillard <d@drobilla.net> Sat, 11 Jun 2011 11:20:11 -0400 | |||
lilv (0.4.2) stable; | |||
* Fix compilation issues on some systems | |||
* Fix build system Python 3 compatibility | |||
-- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||
lilv (0.4.0) stable; | |||
* Initial version (forked from SLV2) | |||
-- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 |
@@ -0,0 +1,29 @@ | |||
This library is designed to allow parallel installation of different major | |||
versions. To facilitate this, the shared library name, include directory, and | |||
pkg-config file are suffixed with the major version number of the library. | |||
For example, if this library was named "foo" and at version 1.x.y: | |||
/usr/include/foo-1/foo/foo.h | |||
/usr/lib/foo-1.so.1.x.y | |||
/usr/lib/pkgconfig/foo-1.pc | |||
Dependencies check for pkg-config name "foo-1" and will build | |||
against a compatible version 1, regardless any other installed versions. | |||
*** IMPORTANT GUIDELINES FOR PACKAGERS *** | |||
Packages should follow the same conventions as above, i.e. include the major | |||
version (and only the major version) in the name of the package. Continuing the | |||
example above, the package(s) would be named foo-1 and foo-1-dev. This way, | |||
if/when version 2 comes out, it may be installed at the same time as version 1 | |||
without breaking anything. | |||
Please do not create packages of this library that do not follow these | |||
guidelines, you will break things and cause unnecessary headaches. Please do | |||
not use any number as a suffix other than the actual major version number of the | |||
upstream source package. | |||
Because program and documentation names are not versioned, these should be | |||
included in separate packages which may replace previous versions, since | |||
there is little use in having parallel installations of them. |
@@ -0,0 +1,11 @@ | |||
Lilv | |||
---- | |||
Lilv is a library for LV2 hosts intended to make using LV2 Plugins as simple | |||
as possible (without sacrificing capabilities). | |||
More information about LV2 plugins can be found at <http://lv2plug.in>. | |||
More information about Lilv can be found at <http://drobilla.net/software/lilv>. | |||
-- David Robillard <d@drobilla.net> |
@@ -0,0 +1,38 @@ | |||
%module lilv | |||
%{ | |||
#include "lilv/lilv.h" | |||
#include "lilv/lilvmm.hpp" | |||
%} | |||
%include "lilv/lilv.h" | |||
%include "lilv/lilvmm.hpp" | |||
namespace Lilv { | |||
%extend Plugins { | |||
%pythoncode %{ | |||
def __iter__(self): | |||
class Iterator(object): | |||
def __init__(self, plugins): | |||
self.plugins = plugins | |||
self.iter = plugins.begin() | |||
def next(self): | |||
self.iter = self.plugins.next(self.iter) | |||
if not self.plugins.is_end(self.iter): | |||
return self.plugins.get(self.iter) | |||
else: | |||
raise StopIteration | |||
return Iterator(self) | |||
%} | |||
}; | |||
%extend Node { | |||
%pythoncode %{ | |||
def __str__(self): | |||
return self.get_turtle_token() | |||
%} | |||
}; | |||
} /* namespace Lilv */ |
@@ -0,0 +1,96 @@ | |||
#!/usr/bin/env python | |||
# -*- coding: utf-8 -*- | |||
import math | |||
import lilv | |||
import sys | |||
import wave | |||
import numpy | |||
# Read command line arguments | |||
if len(sys.argv) != 4: | |||
print 'USAGE: lv2_apply.py PLUGIN_URI INPUT_WAV OUTPUT_WAV' | |||
sys.exit(1) | |||
# Initialise Lilv | |||
world = lilv.World() | |||
world.load_all() | |||
plugin_uri = world.new_uri(sys.argv[1]) | |||
wav_in_path = sys.argv[2] | |||
wav_out_path = sys.argv[3] | |||
# Find plugin | |||
plugin = world.get_all_plugins().get_by_uri(plugin_uri) | |||
if not plugin: | |||
print "Unknown plugin `%s'\n" % plugin_uri | |||
sys.exit(1) | |||
lv2_InputPort = world.new_uri(lilv.LILV_URI_INPUT_PORT) | |||
lv2_OutputPort = world.new_uri(lilv.LILV_URI_OUTPUT_PORT) | |||
lv2_AudioPort = world.new_uri(lilv.LILV_URI_AUDIO_PORT) | |||
n_audio_in = plugin.get_num_ports_of_class(lv2_InputPort, lv2_AudioPort) | |||
n_audio_out = plugin.get_num_ports_of_class(lv2_OutputPort, lv2_AudioPort) | |||
if n_audio_out == 0: | |||
print "Plugin has no audio outputs\n" | |||
sys.exit(1) | |||
# Open input file | |||
wav_in = wave.open(wav_in_path, 'r') | |||
if not wav_in: | |||
print "Failed to open input `%s'\n" % wav_in_path | |||
sys.exit(1) | |||
if wav_in.getnchannels() != n_audio_in: | |||
print "Input has %d channels, but plugin has %d audio inputs\n" % ( | |||
wav_in.getnchannels(), n_audio_in) | |||
sys.exit(1) | |||
# Open output file | |||
wav_out = wave.open(wav_out_path, 'w') | |||
if not wav_out: | |||
print "Failed to open output `%s'\n" % wav_out_path | |||
sys.exit(1) | |||
# Set output file to same format as input (except possibly nchannels) | |||
wav_out.setparams(wav_in.getparams()) | |||
wav_out.setnchannels(n_audio_out) | |||
rate = wav_in.getframerate() | |||
nframes = wav_in.getnframes() | |||
# Instantiate plugin | |||
instance = lilv.Instance(plugin, rate) | |||
def read_float(wf, nframes): | |||
wav = wf.readframes(nframes) | |||
if wf.getsampwidth() == 4: | |||
wav = wave.struct.unpack("<%ul" % (len(wav) / 4), wav) | |||
wav = [ i / float(math.pow(2, 32)) for i in wav ] | |||
elif wf.getsampwidth() == 2: | |||
wav = wave.struct.unpack("<%uh" % (len(wav) / 2), wav) | |||
wav = [ i / float(math.pow(2, 16)) for i in wav ] | |||
else: | |||
wav = wave.struct.unpack("%uB" % (len(wav)), wav) | |||
wav = [ s - 128 for s in wav ] | |||
wav = [ i / float(math.pow(2, 8)) for i in wav ] | |||
n_channels = wf.getnchannels() | |||
wavs = [] | |||
if n_channels > 1: | |||
for i in xrange(n_channels): | |||
wavs.append([ wav[j] for j in xrange(0, len(wav), n_channels) ]) | |||
else: | |||
wavs = [ wav ] | |||
return wavs | |||
in_buf = read_float(wav_in, nframes) | |||
# TODO: buffer marshaling | |||
#instance.connect_port(3, in_buf) | |||
print '%s => %s => %s @ %d Hz' % (wav_in_path, plugin.get_name(), wav_out_path, rate) | |||
instance.connect_port(3, in_buf) |
@@ -0,0 +1,9 @@ | |||
#!/usr/bin/env python | |||
import lilv | |||
world = lilv.World() | |||
world.load_all() | |||
for i in world.get_all_plugins(): | |||
print(i.get_uri()) |
@@ -0,0 +1,33 @@ | |||
.TH LV2INFO 1 "8 Jan 2012" | |||
.SH NAME | |||
.B lv2info \- print information about an installed LV2 plugin. | |||
.SH SYNOPSIS | |||
.B lv2info PLUGIN_URI | |||
.SH OPTIONS | |||
.TP | |||
\fB\-p FILE | |||
Write Turtle description of plugin to FILE | |||
.TP | |||
\fB\-m FILE | |||
Add record of plugin to manifest FILE | |||
.TP | |||
\fB\-\-help\fR | |||
Display help and exit | |||
.TP | |||
\fB\-\-version\fR | |||
Display version information and exit | |||
.SH SEE ALSO | |||
.BR lilv(3), | |||
.BR lv2ls(1) | |||
.SH AUTHOR | |||
lv2info was written by David Robillard <d@drobilla.net> | |||
.PP | |||
This manual page was written by Jaromรญr Mikes <mira.mikes@seznam.cz> | |||
and David Robillard <d@drobilla.net> |
@@ -0,0 +1,30 @@ | |||
.TH LV2LS 1 "17 Jan 2012" | |||
.SH NAME | |||
.B lv2ls \- List all installed LV2 plugins. | |||
.SH SYNOPSIS | |||
.B lv2ls [OPTION]... | |||
.SH OPTIONS | |||
.TP | |||
\fB\-n\fR, \fB\-\-names\fR | |||
Show names instead of URIs | |||
.TP | |||
\fB\-\-help\fR | |||
Display help and exit | |||
.TP | |||
\fB\-\-version\fR | |||
Display version information and exit | |||
.SH SEE ALSO | |||
.BR lilv(3), | |||
.BR lv2info(1) | |||
.SH AUTHOR | |||
lv2ls was written by David Robillard <d@drobilla.net> | |||
.PP | |||
This manual page was written by Jaromรญr Mikes <mira.mikes@seznam.cz> | |||
and David Robillard <d@drobilla.net> |
@@ -0,0 +1,563 @@ | |||
body { | |||
font-size: medium; | |||
font-family: sans-serif; | |||
} | |||
#top { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
padding: 0; | |||
border-bottom: 1px solid #DDD; | |||
margin-bottom: 1ex; | |||
font-size: xx-large; | |||
font-weight: bold; | |||
} | |||
div.header { | |||
display: none; | |||
} | |||
.tabs { | |||
display: none; | |||
} | |||
h1 h2 h3 h4 h5 h6 { | |||
font-weight: bold; | |||
} | |||
h1 { | |||
font-size: 164%; | |||
} | |||
h2 { | |||
font-size: 132%; | |||
} | |||
h3 { | |||
font-size: 124%; | |||
} | |||
h4 { | |||
font-size: 116%; | |||
} | |||
h5 { | |||
font-size: 108%; | |||
} | |||
h6 { | |||
font-size: 100%; | |||
} | |||
p { | |||
margin: 0 0 1ex 0; | |||
} | |||
br { | |||
display: none; | |||
} | |||
dt { | |||
font-weight: 700; | |||
} | |||
div.multicol { | |||
} | |||
p.startli,p.startdd,p.starttd { | |||
margin-top: 2px; | |||
} | |||
p.endli { | |||
margin-bottom: 0; | |||
} | |||
p.enddd { | |||
margin-bottom: 4px; | |||
} | |||
p.endtd { | |||
margin-bottom: 2px; | |||
} | |||
caption { | |||
font-weight: 700; | |||
} | |||
span.legend { | |||
font-size: 70%; | |||
text-align: center; | |||
} | |||
h3.version { | |||
font-size: 90%; | |||
text-align: center; | |||
} | |||
div.qindex,div.navtab { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
padding: 2px; | |||
} | |||
div.qindex,div.navpath { | |||
width: 100%; | |||
line-height: 140%; | |||
} | |||
div.navtab { | |||
margin-right: 15px; | |||
} | |||
/* @group Link Styling */ | |||
a { | |||
color: #3D8C57; | |||
text-decoration: none; | |||
} | |||
.contents a:visited { | |||
color: #50755E; | |||
} | |||
a:hover { | |||
text-decoration: underline; | |||
} | |||
a.qindexHL { | |||
background-color: #9CAFD4; | |||
color: #FFF; | |||
border: 1px double #869DCA; | |||
} | |||
a.code { | |||
color: #4665A2; | |||
} | |||
a.codeRef { | |||
color: #4665A2; | |||
} | |||
/* @end */ | |||
dl.el { | |||
margin-left: -1cm; | |||
} | |||
.fragment { | |||
font-family: monospace, fixed; | |||
font-size: 105%; | |||
} | |||
pre.fragment { | |||
border: 1px solid #C4C4C4; | |||
background-color: #F9F9F9; | |||
padding: 4px 6px; | |||
margin: 4px 8px 4px 2px; | |||
overflow: auto; | |||
font-size: 9pt; | |||
line-height: 125%; | |||
} | |||
div.ah { | |||
background-color: #000; | |||
font-weight: 700; | |||
color: #FFF; | |||
margin-bottom: 3px; | |||
margin-top: 3px; | |||
padding: .2em; | |||
border: thin solid #333; | |||
} | |||
div.groupHeader { | |||
margin-left: 16px; | |||
margin-top: 12px; | |||
margin-bottom: 6px; | |||
font-weight: 700; | |||
} | |||
div.groupText { | |||
margin-left: 16px; | |||
font-style: italic; | |||
} | |||
body { | |||
background: #FFF; | |||
color: #000; | |||
margin: 0; | |||
} | |||
div.contents { | |||
margin-top: 10px; | |||
margin-left: 10px; | |||
margin-right: 10px; | |||
} | |||
td.indexkey { | |||
background-color: #EBEFF6; | |||
font-weight: 700; | |||
border: 1px solid #C4CFE5; | |||
margin: 2px 0; | |||
padding: 2px 10px; | |||
} | |||
td.indexvalue { | |||
background-color: #EBEFF6; | |||
border: 1px solid #C4CFE5; | |||
padding: 2px 10px; | |||
margin: 2px 0; | |||
} | |||
tr.memlist { | |||
background-color: #EEF1F7; | |||
} | |||
p.formulaDsp { | |||
text-align: center; | |||
} | |||
img.formulaDsp { | |||
} | |||
img.formulaInl { | |||
vertical-align: middle; | |||
} | |||
div.center { | |||
text-align: center; | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
padding: 0; | |||
} | |||
div.center img { | |||
border: 0; | |||
} | |||
address.footer { | |||
text-align: right; | |||
padding: 0 0.25em 0.25em 0; | |||
} | |||
img.footer { | |||
border: 0; | |||
vertical-align: middle; | |||
} | |||
/* @group Code Colorization */ | |||
span.keyword { | |||
color: green; | |||
} | |||
span.keywordtype { | |||
color: #604020; | |||
} | |||
span.keywordflow { | |||
color: #e08000; | |||
} | |||
span.comment { | |||
color: maroon; | |||
} | |||
span.preprocessor { | |||
color: #806020; | |||
} | |||
span.stringliteral { | |||
color: #002080; | |||
} | |||
span.charliteral { | |||
color: teal; | |||
} | |||
span.vhdldigit { | |||
color: #F0F; | |||
} | |||
span.vhdlkeyword { | |||
color: #700070; | |||
} | |||
span.vhdllogic { | |||
color: red; | |||
} | |||
/* @end */ | |||
td.tiny { | |||
font-size: 75%; | |||
} | |||
.dirtab { | |||
padding: 4px; | |||
border-collapse: collapse; | |||
border: 1px solid #A3B4D7; | |||
} | |||
th.dirtab { | |||
background: #EBEFF6; | |||
font-weight: 700; | |||
} | |||
hr { | |||
height: 0; | |||
border: none; | |||
border-top: 1px solid #DDD; | |||
margin: 2em 0 1em; | |||
} | |||
hr.footer { | |||
height: 1px; | |||
} | |||
/* @group Member Descriptions */ | |||
table.memberdecls { | |||
border-spacing: 0; | |||
font-size: small; | |||
} | |||
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
background-color: #FBFBFB; | |||
margin: 0; | |||
padding: 0.25ex; | |||
} | |||
.mdescLeft,.mdescRight { | |||
color: #555; | |||
} | |||
.memItemLeft,.memItemRight,.memTemplParams { | |||
border-top: 1px solid #DDD; | |||
} | |||
.memItemLeft,.memTemplItemLeft { | |||
white-space: nowrap; | |||
padding-left: 2em; | |||
} | |||
.memTemplParams { | |||
color: #464646; | |||
white-space: nowrap; | |||
} | |||
/* @end */ | |||
/* @group Member Details */ | |||
/* Styles for detailed member documentation */ | |||
.memtemplate { | |||
font-size: 80%; | |||
color: #4665A2; | |||
font-weight: bold; | |||
} | |||
.memnav { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
margin-right: 15px; | |||
padding: 2px; | |||
} | |||
.memitem { | |||
padding: 0; | |||
margin: 1ex 0 2ex 0; | |||
border: 1px solid #CCC; | |||
} | |||
.memname { | |||
white-space: nowrap; | |||
font-weight: bold; | |||
} | |||
.memproto { | |||
border-bottom: 1px solid #DDD; | |||
padding: 0.5ex; | |||
font-weight: bold; | |||
background-color: #F3F3F3; | |||
} | |||
.memdoc { | |||
padding: 1ex; | |||
background-color: #FBFBFB; | |||
border-top-width: 0; | |||
} | |||
.paramkey { | |||
text-align: right; | |||
} | |||
.paramtype { | |||
white-space: nowrap; | |||
} | |||
.paramname { | |||
color: #602020; | |||
white-space: nowrap; | |||
} | |||
.paramname em { | |||
font-style: normal; | |||
} | |||
/* @end */ | |||
/* @group Directory (tree) */ | |||
/* for the tree view */ | |||
.ftvtree { | |||
font-family: sans-serif; | |||
margin: 0; | |||
} | |||
/* these are for tree view when used as main index */ | |||
.directory { | |||
font-size: 9pt; | |||
font-weight: bold; | |||
margin: 5px; | |||
} | |||
.directory h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory > h3 { | |||
margin-top: 0; | |||
} | |||
.directory p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory img { | |||
vertical-align: -30%; | |||
} | |||
/* these are for tree view when not used as main index */ | |||
.directory-alt { | |||
font-size: 100%; | |||
font-weight: bold; | |||
} | |||
.directory-alt h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory-alt > h3 { | |||
margin-top: 0; | |||
} | |||
.directory-alt p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory-alt div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory-alt img { | |||
vertical-align: -30%; | |||
} | |||
/* @end */ | |||
div.dynheader { | |||
margin-top: 8px; | |||
} | |||
address { | |||
font-style: normal; | |||
color: #2A3D61; | |||
} | |||
table.doxtable { | |||
border-collapse: collapse; | |||
margin: 0.5ex; | |||
} | |||
table.doxtable td,table.doxtable th { | |||
border: 1px solid #DDD; | |||
padding: 3px 7px 2px; | |||
} | |||
table.doxtable th { | |||
background-color: #F3F3F3; | |||
color: #000; | |||
padding-bottom: 4px; | |||
padding-top: 5px; | |||
text-align: left; | |||
font-weight: bold; | |||
} | |||
.tabsearch { | |||
top: 0; | |||
left: 10px; | |||
height: 36px; | |||
z-index: 101; | |||
overflow: hidden; | |||
font-size: 13px; | |||
} | |||
.navpath ul { | |||
font-size: 11px; | |||
height: 30px; | |||
line-height: 30px; | |||
color: #8AA0CC; | |||
border: 1px solid #C2CDE4; | |||
overflow: hidden; | |||
margin: 0; | |||
padding: 0; | |||
} | |||
.navpath li { | |||
list-style-type: none; | |||
float: left; | |||
padding-left: 10px; | |||
padding-right: 15px; | |||
color: #364D7C; | |||
} | |||
.navpath a { | |||
height: 32px; | |||
display: block; | |||
text-decoration: none; | |||
outline: none; | |||
} | |||
.navpath a:hover { | |||
color: #6884BD; | |||
} | |||
div.summary { | |||
float: right; | |||
font-size: 8pt; | |||
padding-right: 5px; | |||
width: 50%; | |||
text-align: right; | |||
} | |||
div.summary a { | |||
white-space: nowrap; | |||
} | |||
div.header { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
border-bottom: 1px solid #DDD; | |||
} | |||
div.headertitle { | |||
padding: 5px 5px 5px 10px; | |||
font-size: 180%; | |||
font-weight: bold; | |||
} |
@@ -0,0 +1,11 @@ | |||
prefix=@PREFIX@ | |||
exec_prefix=@EXEC_PREFIX@ | |||
libdir=@LIBDIR@ | |||
includedir=@INCLUDEDIR@ | |||
Name: Lilv | |||
Version: @LILV_VERSION@ | |||
Description: Simple C library for hosting LV2 plugins | |||
Requires: lv2core @LILV_PKG_DEPS@ | |||
Libs: -L${libdir} -l@LIB_LILV@ -ldl | |||
Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@ |
@@ -0,0 +1,29 @@ | |||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
@prefix : <http://usefulinc.com/ns/doap#> . | |||
@prefix foaf: <http://xmlns.com/foaf/0.1/> . | |||
<http://drobilla.net/software/lilv> | |||
a :Project ; | |||
:bug-database <http://dev.drobilla.net/query?status=new&status=assigned&status=reopened&component=LILV&order=priority> ; | |||
:developer [ | |||
a foaf:Person ; | |||
rdfs:seeAlso <http://drobilla.net/drobilla.rdf> ; | |||
foaf:homepage <http://drobilla.net> ; | |||
foaf:mbox_sha1sum "253b3c58086250260bac1232d744d150274ad308" ; | |||
foaf:name "David Robillard" | |||
] ; | |||
:download-page <http://download.drobilla.net> ; | |||
:homepage <http://drobilla.net/software/lilv> ; | |||
:license <http://usefulinc.com/doap/licenses/gpl> ; | |||
:name "LILV" ; | |||
:programming-language "C", "Turtle" ; | |||
:repository [ | |||
:browse <http://dev.drobilla.net/browser/trunk/lilv> ; | |||
:location <http://svn.drobilla.net/lad/trunk/lilv> ; | |||
a :SVNRepository | |||
] ; | |||
:shortdesc "Library for simple use of LV2 plugins" ; | |||
:shortname "LILV" . | |||
@@ -0,0 +1,332 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef LILV_LILVMM_HPP | |||
#define LILV_LILVMM_HPP | |||
#include "lilv/lilv.h" | |||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) | |||
# define LILV_DEPRECATED __attribute__((__deprecated__)) | |||
#else | |||
# define LILV_DEPRECATED | |||
#endif | |||
namespace Lilv { | |||
static inline const char* | |||
uri_to_path(const char* uri) { | |||
return lilv_uri_to_path(uri); | |||
} | |||
#define LILV_WRAP0(RT, prefix, name) \ | |||
inline RT name() { return lilv_ ## prefix ## _ ## name (me); } | |||
#define LILV_WRAP0_VOID(prefix, name) \ | |||
inline void name() { lilv_ ## prefix ## _ ## name(me); } | |||
#define LILV_WRAP1(RT, prefix, name, T1, a1) \ | |||
inline RT name(T1 a1) { return lilv_ ## prefix ## _ ## name (me, a1); } | |||
#define LILV_WRAP1_VOID(prefix, name, T1, a1) \ | |||
inline void name(T1 a1) { lilv_ ## prefix ## _ ## name(me, a1); } | |||
#define LILV_WRAP2(RT, prefix, name, T1, a1, T2, a2) \ | |||
inline RT name(T1 a1, T2 a2) { \ | |||
return lilv_ ## prefix ## _ ## name(me, a1, a2); \ | |||
} | |||
#define LILV_WRAP2_VOID(prefix, name, T1, a1, T2, a2) \ | |||
inline void name(T1 a1, T2 a2) { lilv_ ## prefix ## _ ## name(me, a1, a2); } | |||
#ifndef SWIG | |||
#define LILV_WRAP_CONVERSION(CT) \ | |||
inline operator CT*() const { return me; } | |||
#else | |||
#define LILV_WRAP_CONVERSION(CT) | |||
#endif | |||
struct Node { | |||
inline Node(LilvNode* node) : me(node) {} | |||
inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {} | |||
inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {} | |||
inline ~Node() { lilv_node_free(me); } | |||
inline bool equals(const Node& other) const { | |||
return lilv_node_equals(me, other.me); | |||
} | |||
inline bool operator==(const Node& other) const { return equals(other); } | |||
LILV_WRAP_CONVERSION(LilvNode); | |||
LILV_WRAP0(char*, node, get_turtle_token); | |||
LILV_WRAP0(bool, node, is_uri); | |||
LILV_WRAP0(const char*, node, as_uri); | |||
LILV_WRAP0(bool, node, is_blank); | |||
LILV_WRAP0(const char*, node, as_blank); | |||
LILV_WRAP0(bool, node, is_literal); | |||
LILV_WRAP0(bool, node, is_string); | |||
LILV_WRAP0(const char*, node, as_string); | |||
LILV_WRAP0(bool, node, is_float); | |||
LILV_WRAP0(float, node, as_float); | |||
LILV_WRAP0(bool, node, is_int); | |||
LILV_WRAP0(int, node, as_int); | |||
LILV_WRAP0(bool, node, is_bool); | |||
LILV_WRAP0(bool, node, as_bool); | |||
LilvNode* me; | |||
}; | |||
struct ScalePoint { | |||
inline ScalePoint(const LilvScalePoint* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvScalePoint); | |||
LILV_WRAP0(const LilvNode*, scale_point, get_label); | |||
LILV_WRAP0(const LilvNode*, scale_point, get_value); | |||
const LilvScalePoint* me; | |||
}; | |||
struct PluginClass { | |||
inline PluginClass(const LilvPluginClass* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvPluginClass); | |||
LILV_WRAP0(Node, plugin_class, get_parent_uri); | |||
LILV_WRAP0(Node, plugin_class, get_uri); | |||
LILV_WRAP0(Node, plugin_class, get_label); | |||
LILV_WRAP0(LilvPluginClasses*, plugin_class, get_children); | |||
const LilvPluginClass* me; | |||
}; | |||
#define LILV_WRAP_COLL(CT, ET, prefix) \ | |||
inline CT(const Lilv ## CT* c_obj) : me(c_obj) {} \ | |||
LILV_WRAP_CONVERSION(const Lilv ## CT); \ | |||
LILV_WRAP0(unsigned, prefix, size); \ | |||
LILV_WRAP1(const ET, prefix, get, LilvIter*, i); \ | |||
LILV_WRAP0(LilvIter*, prefix, begin); \ | |||
LILV_WRAP1(LilvIter*, prefix, next, LilvIter*, i); \ | |||
LILV_WRAP1(bool, prefix, is_end, LilvIter*, i); \ | |||
const Lilv ## CT* me; \ | |||
struct PluginClasses { | |||
LILV_WRAP_COLL(PluginClasses, PluginClass, plugin_classes); | |||
LILV_WRAP1(const PluginClass, plugin_classes, | |||
get_by_uri, const LilvNode*, uri); | |||
}; | |||
struct ScalePoints { | |||
LILV_WRAP_COLL(ScalePoints, ScalePoint, scale_points); | |||
}; | |||
struct Nodes { | |||
LILV_WRAP_COLL(Nodes, Node, nodes); | |||
LILV_WRAP1(bool, nodes, contains, const Node, node); | |||
LILV_WRAP0(Node, nodes, get_first); | |||
}; | |||
struct Port { | |||
inline Port(const LilvPlugin* p, const LilvPort* c_obj) | |||
: parent(p), me(c_obj) | |||
{} | |||
LILV_WRAP_CONVERSION(const LilvPort); | |||
#define LILV_PORT_WRAP0(RT, name) \ | |||
inline RT name () { return lilv_port_ ## name (parent, me); } | |||
#define LILV_PORT_WRAP1(RT, name, T1, a1) \ | |||
inline RT name (T1 a1) { return lilv_port_ ## name (parent, me, a1); } | |||
LILV_PORT_WRAP1(LilvNodes*, get_value, LilvNode*, predicate); | |||
LILV_PORT_WRAP0(LilvNodes*, get_properties) | |||
LILV_PORT_WRAP1(bool, has_property, LilvNode*, property_uri); | |||
LILV_PORT_WRAP1(bool, supports_event, LilvNode*, event_uri); | |||
LILV_PORT_WRAP0(const LilvNode*, get_symbol); | |||
LILV_PORT_WRAP0(LilvNode*, get_name); | |||
LILV_PORT_WRAP0(const LilvNodes*, get_classes); | |||
LILV_PORT_WRAP1(bool, is_a, LilvNode*, port_class); | |||
LILV_PORT_WRAP0(LilvScalePoints*, get_scale_points); | |||
// TODO: get_range (output parameters) | |||
const LilvPlugin* parent; | |||
const LilvPort* me; | |||
}; | |||
struct UI { | |||
inline UI(const LilvUI* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvUI); | |||
LILV_WRAP0(Node, ui, get_uri); | |||
LILV_WRAP1(bool, ui, is_a, LilvNode*, ui_class); | |||
LILV_WRAP0(Node, ui, get_bundle_uri); | |||
LILV_WRAP0(Node, ui, get_binary_uri); | |||
LILV_WRAP0(Nodes, ui, get_supported_features); | |||
LILV_WRAP0(Nodes, ui, get_required_features); | |||
LILV_WRAP0(Nodes, ui, get_optional_features); | |||
LILV_WRAP0(Nodes, ui, get_extension_data); | |||
const LilvUI* me; | |||
}; | |||
struct UIs { | |||
LILV_WRAP_COLL(UIs, UI, uis); | |||
}; | |||
struct Plugin { | |||
inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {} | |||
LILV_WRAP_CONVERSION(const LilvPlugin); | |||
LILV_WRAP0(bool, plugin, verify); | |||
LILV_WRAP0(Node, plugin, get_uri); | |||
LILV_WRAP0(Node, plugin, get_bundle_uri); | |||
LILV_WRAP0(Nodes, plugin, get_data_uris); | |||
LILV_WRAP0(Node, plugin, get_library_uri); | |||
LILV_WRAP0(Node, plugin, get_name); | |||
LILV_WRAP0(PluginClass, plugin, get_class); | |||
LILV_WRAP1(Nodes, plugin, get_value, Node, pred); | |||
LILV_WRAP1(bool, plugin, has_feature, Node, feature_uri); | |||
LILV_WRAP0(Nodes, plugin, get_supported_features); | |||
LILV_WRAP0(Nodes, plugin, get_required_features); | |||
LILV_WRAP0(Nodes, plugin, get_optional_features); | |||
LILV_WRAP0(unsigned, plugin, get_num_ports); | |||
LILV_WRAP0(bool, plugin, has_latency); | |||
LILV_WRAP0(unsigned, plugin, get_latency_port_index); | |||
LILV_WRAP0(Node, plugin, get_author_name); | |||
LILV_WRAP0(Node, plugin, get_author_email); | |||
LILV_WRAP0(Node, plugin, get_author_homepage); | |||
LILV_WRAP0(bool, plugin, is_replaced); | |||
LILV_WRAP0(Nodes, plugin, get_extension_data); | |||
LILV_WRAP0(UIs, plugin, get_uis); | |||
inline Port get_port_by_index(unsigned index) { | |||
return Port(me, lilv_plugin_get_port_by_index(me, index)); | |||
} | |||
inline Port get_port_by_symbol(LilvNode* symbol) { | |||
return Port(me, lilv_plugin_get_port_by_symbol(me, symbol)); | |||
} | |||
inline void get_port_ranges_float(float* min_values, | |||
float* max_values, | |||
float* def_values) { | |||
return lilv_plugin_get_port_ranges_float( | |||
me, min_values, max_values, def_values); | |||
} | |||
inline unsigned get_num_ports_of_class(LilvNode* class_1, | |||
LilvNode* class_2) { | |||
// TODO: varargs | |||
return lilv_plugin_get_num_ports_of_class(me, class_1, class_2, NULL); | |||
} | |||
const LilvPlugin* me; | |||
}; | |||
struct Plugins { | |||
LILV_WRAP_COLL(Plugins, Plugin, plugins); | |||
LILV_WRAP1(const Plugin, plugins, get_by_uri, const LilvNode*, uri); | |||
}; | |||
struct Instance { | |||
inline Instance(LilvInstance* instance) : me(instance) {} | |||
LILV_DEPRECATED | |||
inline Instance(Plugin plugin, double sample_rate) { | |||
me = lilv_plugin_instantiate(plugin, sample_rate, NULL); | |||
} | |||
LILV_DEPRECATED inline Instance(Plugin plugin, | |||
double sample_rate, | |||
LV2_Feature* const* features) { | |||
me = lilv_plugin_instantiate(plugin, sample_rate, features); | |||
} | |||
static inline Instance* create(Plugin plugin, | |||
double sample_rate, | |||
LV2_Feature* const* features) { | |||
LilvInstance* me = lilv_plugin_instantiate( | |||
plugin, sample_rate, features); | |||
return me ? new Instance(me) : NULL; | |||
} | |||
LILV_WRAP_CONVERSION(LilvInstance); | |||
LILV_WRAP2_VOID(instance, connect_port, | |||
unsigned, port_index, | |||
void*, data_location); | |||
LILV_WRAP0_VOID(instance, activate); | |||
LILV_WRAP1_VOID(instance, run, unsigned, sample_count); | |||
LILV_WRAP0_VOID(instance, deactivate); | |||
inline const void* get_extension_data(const char* uri) { | |||
return lilv_instance_get_extension_data(me, uri); | |||
} | |||
inline const LV2_Descriptor* get_descriptor() { | |||
return lilv_instance_get_descriptor(me); | |||
} | |||
inline LV2_Handle get_handle() { | |||
return lilv_instance_get_handle(me); | |||
} | |||
LilvInstance* me; | |||
}; | |||
struct World { | |||
inline World() : me(lilv_world_new()) {} | |||
inline ~World() { lilv_world_free(me); } | |||
inline LilvNode* new_uri(const char* uri) { | |||
return lilv_new_uri(me, uri); | |||
} | |||
inline LilvNode* new_string(const char* str) { | |||
return lilv_new_string(me, str); | |||
} | |||
inline LilvNode* new_int(int val) { | |||
return lilv_new_int(me, val); | |||
} | |||
inline LilvNode* new_float(float val) { | |||
return lilv_new_float(me, val); | |||
} | |||
inline LilvNode* new_bool(bool val) { | |||
return lilv_new_bool(me, val); | |||
} | |||
inline Nodes find_nodes(const LilvNode* subject, | |||
const LilvNode* predicate, | |||
const LilvNode* object) { | |||
return lilv_world_find_nodes(me, subject, predicate, object); | |||
} | |||
LILV_WRAP2_VOID(world, set_option, const char*, uri, LilvNode*, value); | |||
LILV_WRAP0_VOID(world, load_all); | |||
LILV_WRAP1_VOID(world, load_bundle, LilvNode*, bundle_uri); | |||
LILV_WRAP0(const LilvPluginClass*, world, get_plugin_class); | |||
LILV_WRAP0(const LilvPluginClasses*, world, get_plugin_classes); | |||
LILV_WRAP0(const Plugins, world, get_all_plugins); | |||
LilvWorld* me; | |||
}; | |||
} /* namespace Lilv */ | |||
#endif /* LILV_LILVMM_HPP */ |
@@ -0,0 +1,214 @@ | |||
/* | |||
Copyright 2008-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "lilv_internal.h" | |||
int | |||
lilv_ptr_cmp(const void* a, const void* b, void* user_data) | |||
{ | |||
return (intptr_t)a - (intptr_t)b; | |||
} | |||
int | |||
lilv_resource_node_cmp(const void* a, const void* b, void* user_data) | |||
{ | |||
const SordNode* an = ((const LilvNode*)a)->node; | |||
const SordNode* bn = ((const LilvNode*)b)->node; | |||
return (intptr_t)an - (intptr_t)bn; | |||
} | |||
/* Generic collection functions */ | |||
static inline LilvCollection* | |||
lilv_collection_new(ZixComparator cmp, ZixDestroyFunc destructor) | |||
{ | |||
return zix_tree_new(false, cmp, NULL, destructor); | |||
} | |||
void | |||
lilv_collection_free(LilvCollection* coll) | |||
{ | |||
if (coll) | |||
zix_tree_free((ZixTree*)coll); | |||
} | |||
unsigned | |||
lilv_collection_size(const LilvCollection* coll) | |||
{ | |||
return (coll ? zix_tree_size((const ZixTree*)coll) : 0); | |||
} | |||
LilvIter* | |||
lilv_collection_begin(const LilvCollection* collection) | |||
{ | |||
if (collection) { | |||
return (LilvIter*)zix_tree_begin((ZixTree*)collection); | |||
} | |||
return NULL; | |||
} | |||
void* | |||
lilv_collection_get(const LilvCollection* collection, | |||
const LilvIter* i) | |||
{ | |||
return zix_tree_get((ZixTreeIter*)i); | |||
} | |||
/* Constructors */ | |||
LilvScalePoints* | |||
lilv_scale_points_new(void) | |||
{ | |||
return lilv_collection_new(lilv_ptr_cmp, | |||
(ZixDestroyFunc)lilv_scale_point_free); | |||
} | |||
LilvNodes* | |||
lilv_nodes_new(void) | |||
{ | |||
return lilv_collection_new(lilv_ptr_cmp, | |||
(ZixDestroyFunc)lilv_node_free); | |||
} | |||
LilvUIs* | |||
lilv_uis_new(void) | |||
{ | |||
return lilv_collection_new(lilv_header_compare_by_uri, | |||
(ZixDestroyFunc)lilv_ui_free); | |||
} | |||
LilvPluginClasses* | |||
lilv_plugin_classes_new(void) | |||
{ | |||
return lilv_collection_new(lilv_header_compare_by_uri, | |||
(ZixDestroyFunc)lilv_plugin_class_free); | |||
} | |||
/* URI based accessors (for collections of things with URIs) */ | |||
LILV_API | |||
const LilvPluginClass* | |||
lilv_plugin_classes_get_by_uri(const LilvPluginClasses* coll, | |||
const LilvNode* uri) | |||
{ | |||
return (LilvPluginClass*)lilv_collection_get_by_uri( | |||
(const ZixTree*)coll, uri); | |||
} | |||
LILV_API | |||
const LilvUI* | |||
lilv_uis_get_by_uri(const LilvUIs* coll, const LilvNode* uri) | |||
{ | |||
return (LilvUI*)lilv_collection_get_by_uri((const ZixTree*)coll, uri); | |||
} | |||
/* Plugins */ | |||
LilvPlugins* | |||
lilv_plugins_new(void) | |||
{ | |||
return lilv_collection_new(lilv_header_compare_by_uri, NULL); | |||
} | |||
LILV_API | |||
const LilvPlugin* | |||
lilv_plugins_get_by_uri(const LilvPlugins* list, const LilvNode* uri) | |||
{ | |||
return (LilvPlugin*)lilv_collection_get_by_uri((const ZixTree*)list, uri); | |||
} | |||
/* Nodes */ | |||
LILV_API | |||
bool | |||
lilv_nodes_contains(const LilvNodes* list, const LilvNode* value) | |||
{ | |||
LILV_FOREACH(nodes, i, list) | |||
if (lilv_node_equals(lilv_nodes_get(list, i), value)) | |||
return true; | |||
return false; | |||
} | |||
/* Iterator */ | |||
#define LILV_COLLECTION_IMPL(prefix, CT, ET) \ | |||
LILV_API \ | |||
unsigned \ | |||
prefix##_size(const CT* collection) { \ | |||
return lilv_collection_size(collection); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
LilvIter* \ | |||
prefix##_begin(const CT* collection) { \ | |||
return lilv_collection_begin(collection); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
const ET* \ | |||
prefix##_get(const CT* collection, LilvIter* i) { \ | |||
return (ET*)lilv_collection_get(collection, i); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
LilvIter* \ | |||
prefix##_next(const CT* collection, LilvIter* i) { \ | |||
return zix_tree_iter_next((ZixTreeIter*)i); \ | |||
} \ | |||
\ | |||
LILV_API \ | |||
bool \ | |||
prefix##_is_end(const CT* collection, LilvIter* i) { \ | |||
return zix_tree_iter_is_end((ZixTreeIter*)i); \ | |||
} | |||
LILV_COLLECTION_IMPL(lilv_plugin_classes, LilvPluginClasses, LilvPluginClass) | |||
LILV_COLLECTION_IMPL(lilv_scale_points, LilvScalePoints, LilvScalePoint) | |||
LILV_COLLECTION_IMPL(lilv_uis, LilvUIs, LilvUI) | |||
LILV_COLLECTION_IMPL(lilv_nodes, LilvNodes, LilvNode) | |||
LILV_COLLECTION_IMPL(lilv_plugins, LilvPlugins, LilvPlugin) | |||
LILV_API | |||
void | |||
lilv_plugin_classes_free(LilvPluginClasses* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
void | |||
lilv_scale_points_free(LilvScalePoints* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
void | |||
lilv_uis_free(LilvUIs* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
void | |||
lilv_nodes_free(LilvNodes* collection) { | |||
lilv_collection_free(collection); | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_nodes_get_first(const LilvNodes* collection) { | |||
return (LilvNode*)lilv_collection_get(collection, | |||
lilv_collection_begin(collection)); | |||
} |
@@ -0,0 +1,126 @@ | |||
/* | |||
Copyright 2007-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
LILV_API | |||
LilvInstance* | |||
lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
double sample_rate, | |||
const LV2_Feature*const* features) | |||
{ | |||
lilv_plugin_load_if_necessary(plugin); | |||
LilvInstance* result = NULL; | |||
const LV2_Feature** local_features = NULL; | |||
if (features == NULL) { | |||
local_features = (const LV2_Feature**)malloc(sizeof(LV2_Feature)); | |||
local_features[0] = NULL; | |||
} | |||
const LilvNode* const lib_uri = lilv_plugin_get_library_uri(plugin); | |||
const LilvNode* const bundle_uri = lilv_plugin_get_bundle_uri(plugin); | |||
const char* bundle_path = lilv_uri_to_path( | |||
lilv_node_as_uri(lilv_plugin_get_bundle_uri(plugin))); | |||
LilvLib* lib = lilv_lib_open(plugin->world, lib_uri, bundle_path, features); | |||
if (!lib) { | |||
return NULL; | |||
} | |||
// Parse bundle URI to use as base URI | |||
const char* bundle_uri_str = lilv_node_as_uri(bundle_uri); | |||
SerdURI base_uri; | |||
if (serd_uri_parse((const uint8_t*)bundle_uri_str, &base_uri)) { | |||
lilv_lib_close(lib); | |||
return NULL; | |||
} | |||
// Search for plugin by URI | |||
for (uint32_t i = 0; true; ++i) { | |||
const LV2_Descriptor* ld = lilv_lib_get_plugin(lib, i); | |||
if (!ld) { | |||
LILV_ERRORF("No plugin <%s> in <%s>\n", | |||
lilv_node_as_uri(lilv_plugin_get_uri(plugin)), | |||
lilv_node_as_uri(lib_uri)); | |||
lilv_lib_close(lib); | |||
break; // return NULL | |||
} | |||
// Resolve library plugin URI against base URI | |||
SerdURI abs_uri; | |||
SerdNode abs_uri_node = serd_node_new_uri_from_string( | |||
(const uint8_t*)ld->URI, &base_uri, &abs_uri); | |||
if (!abs_uri_node.buf) { | |||
LILV_ERRORF("Failed to parse plugin URI `%s'\n", ld->URI); | |||
lilv_lib_close(lib); | |||
break; | |||
} | |||
if (!strcmp((const char*)abs_uri_node.buf, | |||
lilv_node_as_uri(lilv_plugin_get_uri(plugin)))) { | |||
// Create LilvInstance to return | |||
result = (LilvInstance*)malloc(sizeof(LilvInstance)); | |||
result->lv2_descriptor = ld; | |||
result->lv2_handle = ld->instantiate( | |||
ld, sample_rate, bundle_path, | |||
(features) ? features : local_features); | |||
result->pimpl = lib; | |||
serd_node_free(&abs_uri_node); | |||
break; | |||
} else { | |||
serd_node_free(&abs_uri_node); | |||
} | |||
} | |||
if (result) { | |||
// Failed to instantiate | |||
if (result->lv2_handle == NULL) { | |||
free(result); | |||
return NULL; | |||
} | |||
// "Connect" all ports to NULL (catches bugs) | |||
for (uint32_t i = 0; i < lilv_plugin_get_num_ports(plugin); ++i) | |||
result->lv2_descriptor->connect_port(result->lv2_handle, i, NULL); | |||
} | |||
free(local_features); | |||
return result; | |||
} | |||
LILV_API | |||
void | |||
lilv_instance_free(LilvInstance* instance) | |||
{ | |||
if (!instance) | |||
return; | |||
instance->lv2_descriptor->cleanup(instance->lv2_handle); | |||
instance->lv2_descriptor = NULL; | |||
lilv_lib_close((LilvLib*)instance->pimpl); | |||
instance->pimpl = NULL; | |||
free(instance); | |||
} | |||
@@ -0,0 +1,111 @@ | |||
/* | |||
Copyright 2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "lilv_internal.h" | |||
LilvLib* | |||
lilv_lib_open(LilvWorld* world, | |||
const LilvNode* uri, | |||
const char* bundle_path, | |||
const LV2_Feature*const* features) | |||
{ | |||
ZixTreeIter* i = NULL; | |||
const struct LilvHeader key = { world, (LilvNode*)uri }; | |||
if (!zix_tree_find(world->libs, &key, &i)) { | |||
LilvLib* llib = (LilvLib*)zix_tree_get(i); | |||
++llib->refs; | |||
return llib; | |||
} | |||
const char* const lib_uri = lilv_node_as_uri(uri); | |||
const char* const lib_path = lilv_uri_to_path(lib_uri); | |||
if (!lib_path) { | |||
return NULL; | |||
} | |||
dlerror(); | |||
void* lib = dlopen(lib_path, RTLD_NOW); | |||
if (!lib) { | |||
LILV_ERRORF("Failed to open library %s (%s)\n", lib_path, dlerror()); | |||
return NULL; | |||
} | |||
LV2_Descriptor_Function df = (LV2_Descriptor_Function) | |||
lilv_dlfunc(lib, "lv2_descriptor"); | |||
#ifdef LILV_NEW_LV2 | |||
LV2_Lib_Descriptor_Function ldf = (LV2_Lib_Descriptor_Function) | |||
lilv_dlfunc(lib, "lv2_lib_descriptor"); | |||
const LV2_Lib_Descriptor* desc = NULL; | |||
if (ldf) { | |||
desc = ldf(bundle_path, features); | |||
if (!desc) { | |||
LILV_ERRORF("Call to `lv2_lib_descriptor' in %s failed\n", lib_path); | |||
return NULL; | |||
} | |||
} else | |||
#endif | |||
if (!df) { | |||
LILV_ERRORF("No `lv2_descriptor' or `lv2_lib_descriptor' in %s\n", | |||
lib_path); | |||
dlclose(lib); | |||
return NULL; | |||
} | |||
LilvLib* llib = (LilvLib*)malloc(sizeof(LilvLib)); | |||
llib->world = world; | |||
llib->uri = lilv_node_duplicate(uri); | |||
llib->lib = lib; | |||
llib->lv2_descriptor = df; | |||
#ifdef LILV_NEW_LV2 | |||
llib->desc = desc; | |||
#endif | |||
llib->refs = 1; | |||
zix_tree_insert(world->libs, llib, NULL); | |||
return llib; | |||
} | |||
const LV2_Descriptor* | |||
lilv_lib_get_plugin(LilvLib* lib, uint32_t index) | |||
{ | |||
if (lib->lv2_descriptor) { | |||
return lib->lv2_descriptor(index); | |||
} | |||
#ifdef LILV_NEW_LV2 | |||
if (lib->desc) { | |||
return lib->desc->get_plugin(lib->desc->handle, index); | |||
} | |||
#endif | |||
return NULL; | |||
} | |||
void | |||
lilv_lib_close(LilvLib* lib) | |||
{ | |||
if (--lib->refs == 0) { | |||
dlclose(lib->lib); | |||
ZixTreeIter* i = NULL; | |||
if (lib->world->libs && !zix_tree_find(lib->world->libs, lib, &i)) { | |||
zix_tree_remove(lib->world->libs, i); | |||
} | |||
lilv_node_free(lib->uri); | |||
free(lib); | |||
} | |||
} |
@@ -0,0 +1,382 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef LILV_INTERNAL_H | |||
#define LILV_INTERNAL_H | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <float.h> | |||
#ifdef _WIN32 | |||
# include <windows.h> | |||
# define dlopen(path, flags) LoadLibrary(path) | |||
# define dlclose(lib) FreeLibrary((HMODULE)lib) | |||
# define dlsym GetProcAddress | |||
# ifdef _MSC_VER | |||
# define __func__ __FUNCTION__ | |||
# define INFINITY DBL_MAX + DBL_MAX | |||
# define NAN INFINITY - INFINITY | |||
# define snprintf _snprintf | |||
# endif | |||
static inline char* dlerror(void) { return "Unknown error"; } | |||
#else | |||
# include <dlfcn.h> | |||
#endif | |||
#include "serd/serd.h" | |||
#include "sord/sord.h" | |||
#include "zix/tree.h" | |||
#include "lilv_config.h" | |||
#include "lilv/lilv.h" | |||
#ifdef LILV_DYN_MANIFEST | |||
# include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h" | |||
#endif | |||
/* | |||
* | |||
* Types | |||
* | |||
*/ | |||
typedef struct LilvSpecImpl LilvSpec; | |||
typedef void LilvCollection; | |||
struct LilvPortImpl { | |||
SordNode* node; ///< RDF node | |||
uint32_t index; ///< lv2:index | |||
LilvNode* symbol; ///< lv2:symbol | |||
LilvNodes* classes; ///< rdf:type | |||
}; | |||
struct LilvSpecImpl { | |||
SordNode* spec; | |||
SordNode* bundle; | |||
LilvNodes* data_uris; | |||
struct LilvSpecImpl* next; | |||
}; | |||
/** | |||
Header of an LilvPlugin, LilvPluginClass, or LilvUI. | |||
Any of these structs may be safely casted to LilvHeader, which is used to | |||
implement collections using the same comparator. | |||
*/ | |||
struct LilvHeader { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
}; | |||
#ifdef LILV_DYN_MANIFEST | |||
typedef struct { | |||
LilvNode* bundle; | |||
void* lib; | |||
LV2_Dyn_Manifest_Handle handle; | |||
uint32_t refs; | |||
} LilvDynManifest; | |||
#endif | |||
typedef struct { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
void* lib; | |||
LV2_Descriptor_Function lv2_descriptor; | |||
#ifdef LILV_NEW_LV2 | |||
const LV2_Lib_Descriptor* desc; | |||
#endif | |||
uint32_t refs; | |||
} LilvLib; | |||
struct LilvPluginImpl { | |||
LilvWorld* world; | |||
LilvNode* plugin_uri; | |||
LilvNode* bundle_uri; ///< Bundle plugin was loaded from | |||
LilvNode* binary_uri; ///< lv2:binary | |||
#ifdef LILV_DYN_MANIFEST | |||
LilvDynManifest* dynmanifest; | |||
#endif | |||
const LilvPluginClass* plugin_class; | |||
LilvNodes* data_uris; ///< rdfs::seeAlso | |||
LilvPort** ports; | |||
uint32_t num_ports; | |||
bool loaded; | |||
bool replaced; | |||
}; | |||
struct LilvPluginClassImpl { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
LilvNode* parent_uri; | |||
LilvNode* label; | |||
}; | |||
struct LilvInstancePimpl { | |||
LilvWorld* world; | |||
LilvLib* lib; | |||
}; | |||
typedef struct { | |||
bool dyn_manifest; | |||
bool filter_language; | |||
} LilvOptions; | |||
struct LilvWorldImpl { | |||
SordWorld* world; | |||
SordModel* model; | |||
SerdReader* reader; | |||
unsigned n_read_files; | |||
LilvPluginClass* lv2_plugin_class; | |||
LilvPluginClasses* plugin_classes; | |||
LilvSpec* specs; | |||
LilvPlugins* plugins; | |||
LilvNodes* loaded_files; | |||
ZixTree* libs; | |||
struct { | |||
SordNode* dc_replaces; | |||
SordNode* dman_DynManifest; | |||
SordNode* doap_name; | |||
SordNode* lv2_Plugin; | |||
SordNode* lv2_Specification; | |||
SordNode* lv2_appliesTo; | |||
SordNode* lv2_binary; | |||
SordNode* lv2_default; | |||
SordNode* lv2_designation; | |||
SordNode* lv2_extensionData; | |||
SordNode* lv2_index; | |||
SordNode* lv2_maximum; | |||
SordNode* lv2_minimum; | |||
SordNode* lv2_name; | |||
SordNode* lv2_optionalFeature; | |||
SordNode* lv2_port; | |||
SordNode* lv2_portProperty; | |||
SordNode* lv2_reportsLatency; | |||
SordNode* lv2_requiredFeature; | |||
SordNode* lv2_symbol; | |||
SordNode* null_uri; | |||
SordNode* pset_value; | |||
SordNode* rdf_a; | |||
SordNode* rdf_value; | |||
SordNode* rdfs_Class; | |||
SordNode* rdfs_label; | |||
SordNode* rdfs_seeAlso; | |||
SordNode* rdfs_subClassOf; | |||
SordNode* xsd_base64Binary; | |||
SordNode* xsd_boolean; | |||
SordNode* xsd_decimal; | |||
SordNode* xsd_double; | |||
SordNode* xsd_integer; | |||
} uris; | |||
LilvOptions opt; | |||
}; | |||
typedef enum { | |||
LILV_VALUE_URI, | |||
LILV_VALUE_STRING, | |||
LILV_VALUE_INT, | |||
LILV_VALUE_FLOAT, | |||
LILV_VALUE_BOOL, | |||
LILV_VALUE_BLANK, | |||
LILV_VALUE_BLOB | |||
} LilvNodeType; | |||
struct LilvNodeImpl { | |||
LilvWorld* world; | |||
SordNode* node; | |||
LilvNodeType type; | |||
union { | |||
int int_val; | |||
float float_val; | |||
bool bool_val; | |||
} val; | |||
}; | |||
struct LilvScalePointImpl { | |||
LilvNode* value; | |||
LilvNode* label; | |||
}; | |||
struct LilvUIImpl { | |||
LilvWorld* world; | |||
LilvNode* uri; | |||
LilvNode* bundle_uri; | |||
LilvNode* binary_uri; | |||
LilvNodes* classes; | |||
}; | |||
/* | |||
* | |||
* Functions | |||
* | |||
*/ | |||
LilvPort* lilv_port_new(LilvWorld* world, | |||
const SordNode* node, | |||
uint32_t index, | |||
const char* symbol); | |||
void lilv_port_free(const LilvPlugin* plugin, LilvPort* port); | |||
LilvPlugin* lilv_plugin_new(LilvWorld* world, | |||
LilvNode* uri, | |||
LilvNode* bundle_uri); | |||
void lilv_plugin_load_if_necessary(const LilvPlugin* p); | |||
void lilv_plugin_free(LilvPlugin* plugin); | |||
LilvNode* lilv_plugin_get_unique(const LilvPlugin* p, | |||
const SordNode* subject, | |||
const SordNode* predicate); | |||
void lilv_collection_free(LilvCollection* collection); | |||
unsigned lilv_collection_size(const LilvCollection* collection); | |||
LilvIter* lilv_collection_begin(const LilvCollection* collection); | |||
void* lilv_collection_get(const LilvCollection* collection, | |||
const LilvIter* i); | |||
LilvPluginClass* lilv_plugin_class_new(LilvWorld* world, | |||
const SordNode* parent_uri, | |||
const SordNode* uri, | |||
const char* label); | |||
void lilv_plugin_class_free(LilvPluginClass* plugin_class); | |||
LilvLib* | |||
lilv_lib_open(LilvWorld* world, | |||
const LilvNode* uri, | |||
const char* bundle_path, | |||
const LV2_Feature*const* features); | |||
const LV2_Descriptor* lilv_lib_get_plugin(LilvLib* lib, uint32_t index); | |||
void lilv_lib_close(LilvLib* lib); | |||
LilvNodes* lilv_nodes_new(void); | |||
LilvPlugins* lilv_plugins_new(void); | |||
LilvScalePoints* lilv_scale_points_new(void); | |||
LilvPluginClasses* lilv_plugin_classes_new(void); | |||
LilvUIs* lilv_uis_new(void); | |||
const uint8_t* lilv_world_blank_node_prefix(LilvWorld* world); | |||
void lilv_world_load_file(LilvWorld* world, const char* file_uri); | |||
LilvUI* lilv_ui_new(LilvWorld* world, | |||
LilvNode* uri, | |||
LilvNode* type_uri, | |||
LilvNode* binary_uri); | |||
void lilv_ui_free(LilvUI* ui); | |||
LilvNode* lilv_node_new(LilvWorld* world, | |||
LilvNodeType type, | |||
const char* val); | |||
LilvNode* lilv_node_new_from_node(LilvWorld* world, | |||
const SordNode* node); | |||
const SordNode* lilv_node_as_node(const LilvNode* value); | |||
int lilv_header_compare_by_uri(const void* a, const void* b, void* user_data); | |||
int | |||
lilv_ptr_cmp(const void* a, const void* b, void* user_data); | |||
int | |||
lilv_resource_node_cmp(const void* a, const void* b, void* user_data); | |||
struct LilvHeader* | |||
lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri); | |||
LilvScalePoint* lilv_scale_point_new(LilvNode* value, LilvNode* label); | |||
void lilv_scale_point_free(LilvScalePoint* point); | |||
SordIter* | |||
lilv_world_query_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object); | |||
LilvNodes* | |||
lilv_world_query_values_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object); | |||
#define FOREACH_MATCH(iter) \ | |||
for (; !sord_iter_end(iter); sord_iter_next(iter)) | |||
LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* w, | |||
SordIter* stream, | |||
SordQuadIndex field); | |||
char* lilv_strjoin(const char* first, ...); | |||
char* lilv_strdup(const char* str); | |||
char* lilv_get_lang(void); | |||
char* lilv_expand(const char* path); | |||
char* lilv_dirname(const char* path); | |||
int lilv_copy_file(const char* src, const char* dst); | |||
bool lilv_path_exists(const char* path, void* ignored); | |||
char* lilv_path_absolute(const char* path); | |||
bool lilv_path_is_absolute(const char* path); | |||
char* lilv_get_latest_copy(const char* path, const char* copy_path); | |||
char* lilv_path_relative_to(const char* path, const char* base); | |||
bool lilv_path_is_child(const char* path, const char* dir); | |||
int lilv_flock(FILE* file, bool lock); | |||
char* lilv_realpath(const char* path); | |||
int lilv_symlink(const char* oldpath, const char* newpath); | |||
int lilv_mkdir_p(const char* path); | |||
char* lilv_path_join(const char* a, const char* b); | |||
bool lilv_file_equals(const char* a_path, const char* b_path); | |||
char* | |||
lilv_find_free_path(const char* in_path, | |||
bool (*exists)(const char*, void*), void* user_data); | |||
void | |||
lilv_dir_for_each(const char* path, | |||
void* data, | |||
void (*f)(const char* path, const char* name, void* data)); | |||
typedef void (*VoidFunc)(void); | |||
/** dlsym wrapper to return a function pointer (without annoying warning) */ | |||
static inline VoidFunc | |||
lilv_dlfunc(void* handle, const char* symbol) | |||
{ | |||
typedef VoidFunc (*VoidFuncGetter)(void*, const char*); | |||
VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym; | |||
return dlfunc(handle, symbol); | |||
} | |||
#ifdef LILV_DYN_MANIFEST | |||
static const LV2_Feature* const dman_features = { NULL }; | |||
#endif | |||
#define LILV_ERROR(str) fprintf(stderr, "%s(): error: " str, \ | |||
__func__) | |||
#define LILV_ERRORF(fmt, ...) fprintf(stderr, "%s(): error: " fmt, \ | |||
__func__, __VA_ARGS__) | |||
#define LILV_WARN(str) fprintf(stderr, "%s(): warning: " str, \ | |||
__func__) | |||
#define LILV_WARNF(fmt, ...) fprintf(stderr, "%s(): warning: " fmt, \ | |||
__func__, __VA_ARGS__) | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif /* LILV_INTERNAL_H */ |
@@ -0,0 +1,396 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
static void | |||
lilv_node_set_numerics_from_string(LilvNode* val, size_t len) | |||
{ | |||
const char* str = (const char*)sord_node_get_string(val->node); | |||
char* endptr; | |||
switch (val->type) { | |||
case LILV_VALUE_URI: | |||
case LILV_VALUE_BLANK: | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_BLOB: | |||
break; | |||
case LILV_VALUE_INT: | |||
val->val.int_val = strtol(str, &endptr, 10); | |||
break; | |||
case LILV_VALUE_FLOAT: | |||
val->val.float_val = serd_strtod(str, &endptr); | |||
break; | |||
case LILV_VALUE_BOOL: | |||
val->val.bool_val = !strcmp(str, "true"); | |||
break; | |||
} | |||
} | |||
/** Note that if @a type is numeric or boolean, the returned value is corrupt | |||
* until lilv_node_set_numerics_from_string is called. It is not | |||
* automatically called from here to avoid overhead and imprecision when the | |||
* exact string value is known. | |||
*/ | |||
LilvNode* | |||
lilv_node_new(LilvWorld* world, LilvNodeType type, const char* str) | |||
{ | |||
LilvNode* val = (LilvNode*)malloc(sizeof(LilvNode)); | |||
val->world = world; | |||
val->type = type; | |||
const uint8_t* ustr = (const uint8_t*)str; | |||
switch (type) { | |||
case LILV_VALUE_URI: | |||
val->node = sord_new_uri(world->world, ustr); | |||
break; | |||
case LILV_VALUE_BLANK: | |||
val->node = sord_new_blank(world->world, ustr); | |||
break; | |||
case LILV_VALUE_STRING: | |||
val->node = sord_new_literal(world->world, NULL, ustr, NULL); | |||
break; | |||
case LILV_VALUE_INT: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_integer, ustr, NULL); | |||
break; | |||
case LILV_VALUE_FLOAT: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_decimal, ustr, NULL); | |||
break; | |||
case LILV_VALUE_BOOL: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_boolean, ustr, NULL); | |||
break; | |||
case LILV_VALUE_BLOB: | |||
val->node = sord_new_literal( | |||
world->world, world->uris.xsd_base64Binary, ustr, NULL); | |||
break; | |||
} | |||
return val; | |||
} | |||
/** Create a new LilvNode from @a node, or return NULL if impossible */ | |||
LilvNode* | |||
lilv_node_new_from_node(LilvWorld* world, const SordNode* node) | |||
{ | |||
LilvNode* result = NULL; | |||
SordNode* datatype_uri = NULL; | |||
LilvNodeType type = LILV_VALUE_STRING; | |||
size_t len = 0; | |||
switch (sord_node_get_type(node)) { | |||
case SORD_URI: | |||
result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
result->world = (LilvWorld*)world; | |||
result->type = LILV_VALUE_URI; | |||
result->node = sord_node_copy(node); | |||
break; | |||
case SORD_BLANK: | |||
result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
result->world = (LilvWorld*)world; | |||
result->type = LILV_VALUE_BLANK; | |||
result->node = sord_node_copy(node); | |||
break; | |||
case SORD_LITERAL: | |||
datatype_uri = sord_node_get_datatype(node); | |||
if (datatype_uri) { | |||
if (sord_node_equals(datatype_uri, world->uris.xsd_boolean)) | |||
type = LILV_VALUE_BOOL; | |||
else if (sord_node_equals(datatype_uri, world->uris.xsd_decimal) | |||
|| sord_node_equals(datatype_uri, world->uris.xsd_double)) | |||
type = LILV_VALUE_FLOAT; | |||
else if (sord_node_equals(datatype_uri, world->uris.xsd_integer)) | |||
type = LILV_VALUE_INT; | |||
else if (sord_node_equals(datatype_uri, | |||
world->uris.xsd_base64Binary)) | |||
type = LILV_VALUE_BLOB; | |||
else | |||
LILV_ERRORF("Unknown datatype `%s'\n", | |||
sord_node_get_string(datatype_uri)); | |||
} | |||
result = lilv_node_new( | |||
world, type, (const char*)sord_node_get_string_counted(node, &len)); | |||
lilv_node_set_numerics_from_string(result, len); | |||
break; | |||
default: | |||
assert(false); | |||
} | |||
return result; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_uri(LilvWorld* world, const char* uri) | |||
{ | |||
return lilv_node_new(world, LILV_VALUE_URI, uri); | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_string(LilvWorld* world, const char* str) | |||
{ | |||
return lilv_node_new(world, LILV_VALUE_STRING, str); | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_int(LilvWorld* world, int val) | |||
{ | |||
char str[32]; | |||
snprintf(str, sizeof(str), "%d", val); | |||
LilvNode* ret = lilv_node_new(world, LILV_VALUE_INT, str); | |||
ret->val.int_val = val; | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_float(LilvWorld* world, float val) | |||
{ | |||
char str[32]; | |||
snprintf(str, sizeof(str), "%f", val); | |||
LilvNode* ret = lilv_node_new(world, LILV_VALUE_FLOAT, str); | |||
ret->val.float_val = val; | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_new_bool(LilvWorld* world, bool val) | |||
{ | |||
LilvNode* ret = lilv_node_new(world, LILV_VALUE_BOOL, | |||
val ? "true" : "false"); | |||
ret->val.bool_val = val; | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_node_duplicate(const LilvNode* val) | |||
{ | |||
if (!val) { | |||
return NULL; | |||
} | |||
LilvNode* result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
result->world = val->world; | |||
result->node = sord_node_copy(val->node); | |||
result->val = val->val; | |||
result->type = val->type; | |||
return result; | |||
} | |||
LILV_API | |||
void | |||
lilv_node_free(LilvNode* val) | |||
{ | |||
if (val) { | |||
sord_node_free(val->world->world, val->node); | |||
free(val); | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_equals(const LilvNode* value, const LilvNode* other) | |||
{ | |||
if (value == NULL && other == NULL) | |||
return true; | |||
else if (value == NULL || other == NULL) | |||
return false; | |||
else if (value->type != other->type) | |||
return false; | |||
switch (value->type) { | |||
case LILV_VALUE_URI: | |||
case LILV_VALUE_BLANK: | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_BLOB: | |||
return sord_node_equals(value->node, other->node); | |||
case LILV_VALUE_INT: | |||
return (value->val.int_val == other->val.int_val); | |||
case LILV_VALUE_FLOAT: | |||
return (value->val.float_val == other->val.float_val); | |||
case LILV_VALUE_BOOL: | |||
return (value->val.bool_val == other->val.bool_val); | |||
} | |||
return false; /* shouldn't get here */ | |||
} | |||
LILV_API | |||
char* | |||
lilv_node_get_turtle_token(const LilvNode* value) | |||
{ | |||
const char* str = (const char*)sord_node_get_string(value->node); | |||
size_t len = 0; | |||
char* result = NULL; | |||
SerdNode node; | |||
switch (value->type) { | |||
case LILV_VALUE_URI: | |||
len = strlen(str) + 3; | |||
result = (char*)calloc(len, 1); | |||
snprintf(result, len, "<%s>", str); | |||
break; | |||
case LILV_VALUE_BLANK: | |||
len = strlen(str) + 3; | |||
result = (char*)calloc(len, 1); | |||
snprintf(result, len, "_:%s", str); | |||
break; | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_BOOL: | |||
case LILV_VALUE_BLOB: | |||
result = lilv_strdup(str); | |||
break; | |||
case LILV_VALUE_INT: | |||
node = serd_node_new_integer(value->val.int_val); | |||
result = (char*)node.buf; | |||
break; | |||
case LILV_VALUE_FLOAT: | |||
node = serd_node_new_decimal(value->val.float_val, 8); | |||
result = (char*)node.buf; | |||
break; | |||
} | |||
return result; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_uri(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_URI); | |||
} | |||
LILV_API | |||
const char* | |||
lilv_node_as_uri(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_uri(value)); | |||
return (const char*)sord_node_get_string(value->node); | |||
} | |||
const SordNode* | |||
lilv_node_as_node(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_uri(value)); | |||
return value->node; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_blank(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_BLANK); | |||
} | |||
LILV_API | |||
const char* | |||
lilv_node_as_blank(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_blank(value)); | |||
return (const char*)sord_node_get_string(value->node); | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_literal(const LilvNode* value) | |||
{ | |||
if (!value) | |||
return false; | |||
switch (value->type) { | |||
case LILV_VALUE_STRING: | |||
case LILV_VALUE_INT: | |||
case LILV_VALUE_FLOAT: | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_string(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_STRING); | |||
} | |||
LILV_API | |||
const char* | |||
lilv_node_as_string(const LilvNode* value) | |||
{ | |||
return value ? (const char*)sord_node_get_string(value->node) : NULL; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_int(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_INT); | |||
} | |||
LILV_API | |||
int | |||
lilv_node_as_int(const LilvNode* value) | |||
{ | |||
assert(value); | |||
assert(lilv_node_is_int(value)); | |||
return value->val.int_val; | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_float(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_FLOAT); | |||
} | |||
LILV_API | |||
float | |||
lilv_node_as_float(const LilvNode* value) | |||
{ | |||
assert(lilv_node_is_float(value) || lilv_node_is_int(value)); | |||
if (lilv_node_is_float(value)) { | |||
return value->val.float_val; | |||
} else { // lilv_node_is_int(value) | |||
return (float)value->val.int_val; | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_is_bool(const LilvNode* value) | |||
{ | |||
return (value && value->type == LILV_VALUE_BOOL); | |||
} | |||
LILV_API | |||
bool | |||
lilv_node_as_bool(const LilvNode* value) | |||
{ | |||
assert(value); | |||
assert(lilv_node_is_bool(value)); | |||
return value->val.bool_val; | |||
} |
@@ -0,0 +1,96 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
LilvPluginClass* | |||
lilv_plugin_class_new(LilvWorld* world, | |||
const SordNode* parent_node, | |||
const SordNode* uri, | |||
const char* label) | |||
{ | |||
if (parent_node && sord_node_get_type(parent_node) != SORD_URI) { | |||
return NULL; // Not an LV2 plugin superclass (FIXME: discover properly) | |||
} | |||
LilvPluginClass* pc = (LilvPluginClass*)malloc(sizeof(LilvPluginClass)); | |||
pc->world = world; | |||
pc->uri = lilv_node_new_from_node(world, uri); | |||
pc->label = lilv_node_new(world, LILV_VALUE_STRING, label); | |||
pc->parent_uri = (parent_node) | |||
? lilv_node_new_from_node(world, parent_node) | |||
: NULL; | |||
return pc; | |||
} | |||
void | |||
lilv_plugin_class_free(LilvPluginClass* plugin_class) | |||
{ | |||
assert(plugin_class->uri); | |||
lilv_node_free(plugin_class->uri); | |||
lilv_node_free(plugin_class->parent_uri); | |||
lilv_node_free(plugin_class->label); | |||
free(plugin_class); | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_plugin_class_get_parent_uri(const LilvPluginClass* plugin_class) | |||
{ | |||
if (plugin_class->parent_uri) | |||
return plugin_class->parent_uri; | |||
else | |||
return NULL; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class) | |||
{ | |||
assert(plugin_class->uri); | |||
return plugin_class->uri; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_plugin_class_get_label(const LilvPluginClass* plugin_class) | |||
{ | |||
return plugin_class->label; | |||
} | |||
LILV_API | |||
LilvPluginClasses* | |||
lilv_plugin_class_get_children(const LilvPluginClass* plugin_class) | |||
{ | |||
// Returned list doesn't own categories | |||
LilvPluginClasses* all = plugin_class->world->plugin_classes; | |||
LilvPluginClasses* result = zix_tree_new(false, lilv_ptr_cmp, NULL, NULL); | |||
for (ZixTreeIter* i = zix_tree_begin((ZixTree*)all); | |||
i != zix_tree_end((ZixTree*)all); | |||
i = zix_tree_iter_next(i)) { | |||
const LilvPluginClass* c = (LilvPluginClass*)zix_tree_get(i); | |||
const LilvNode* parent = lilv_plugin_class_get_parent_uri(c); | |||
if (parent && lilv_node_equals(lilv_plugin_class_get_uri(plugin_class), | |||
parent)) | |||
zix_tree_insert((ZixTree*)result, (LilvPluginClass*)c, NULL); | |||
} | |||
return result; | |||
} |
@@ -0,0 +1,264 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <limits.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/event.h" | |||
#include "lilv_internal.h" | |||
LilvPort* | |||
lilv_port_new(LilvWorld* world, | |||
const SordNode* node, | |||
uint32_t index, | |||
const char* symbol) | |||
{ | |||
LilvPort* port = (LilvPort*)malloc(sizeof(LilvPort)); | |||
port->node = sord_node_copy(node); | |||
port->index = index; | |||
port->symbol = lilv_node_new(world, LILV_VALUE_STRING, symbol); | |||
port->classes = lilv_nodes_new(); | |||
return port; | |||
} | |||
void | |||
lilv_port_free(const LilvPlugin* plugin, LilvPort* port) | |||
{ | |||
if (port) { | |||
sord_node_free(plugin->world->world, port->node); | |||
lilv_nodes_free(port->classes); | |||
lilv_node_free(port->symbol); | |||
free(port); | |||
} | |||
} | |||
LILV_API | |||
bool | |||
lilv_port_is_a(const LilvPlugin* plugin, | |||
const LilvPort* port, | |||
const LilvNode* port_class) | |||
{ | |||
LILV_FOREACH(nodes, i, port->classes) | |||
if (lilv_node_equals(lilv_nodes_get(port->classes, i), port_class)) | |||
return true; | |||
return false; | |||
} | |||
LILV_API | |||
bool | |||
lilv_port_has_property(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const LilvNode* property) | |||
{ | |||
assert(property); | |||
SordIter* results = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
p->world->uris.lv2_portProperty, | |||
lilv_node_as_node(property)); | |||
const bool ret = !sord_iter_end(results); | |||
sord_iter_free(results); | |||
return ret; | |||
} | |||
LILV_API | |||
bool | |||
lilv_port_supports_event(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const LilvNode* event) | |||
{ | |||
assert(event); | |||
SordIter* results = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
sord_new_uri(p->world->world, (const uint8_t*)LV2_EVENT__supportsEvent), | |||
lilv_node_as_node(event)); | |||
const bool ret = !sord_iter_end(results); | |||
sord_iter_free(results); | |||
return ret; | |||
} | |||
static LilvNodes* | |||
lilv_port_get_value_by_node(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const SordNode* predicate) | |||
{ | |||
assert(sord_node_get_type(predicate) == SORD_URI); | |||
SordIter* results = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
predicate, | |||
NULL); | |||
return lilv_nodes_from_stream_objects(p->world, results, SORD_OBJECT); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_port_get_value(const LilvPlugin* p, | |||
const LilvPort* port, | |||
const LilvNode* predicate) | |||
{ | |||
if (!lilv_node_is_uri(predicate)) { | |||
LILV_ERRORF("Predicate `%s' is not a URI\n", | |||
sord_node_get_string(predicate->node)); | |||
return NULL; | |||
} | |||
return lilv_port_get_value_by_node( | |||
p, port, | |||
lilv_node_as_node(predicate)); | |||
} | |||
LILV_API | |||
uint32_t | |||
lilv_port_get_index(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
return port->index; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_port_get_symbol(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
return port->symbol; | |||
} | |||
LILV_API | |||
LilvNode* | |||
lilv_port_get_name(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
LilvNodes* results = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_name); | |||
LilvNode* ret = NULL; | |||
if (results) { | |||
LilvNode* val = lilv_nodes_get_first(results); | |||
if (lilv_node_is_string(val)) | |||
ret = lilv_node_duplicate(val); | |||
lilv_nodes_free(results); | |||
} | |||
if (!ret) | |||
LILV_WARNF("Plugin <%s> port has no (mandatory) doap:name\n", | |||
lilv_node_as_string(lilv_plugin_get_uri(p))); | |||
return ret; | |||
} | |||
LILV_API | |||
const LilvNodes* | |||
lilv_port_get_classes(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
return port->classes; | |||
} | |||
LILV_API | |||
void | |||
lilv_port_get_range(const LilvPlugin* p, | |||
const LilvPort* port, | |||
LilvNode** def, | |||
LilvNode** min, | |||
LilvNode** max) | |||
{ | |||
if (def) { | |||
LilvNodes* defaults = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_default); | |||
*def = defaults | |||
? lilv_node_duplicate(lilv_nodes_get_first(defaults)) | |||
: NULL; | |||
lilv_nodes_free(defaults); | |||
} | |||
if (min) { | |||
LilvNodes* minimums = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_minimum); | |||
*min = minimums | |||
? lilv_node_duplicate(lilv_nodes_get_first(minimums)) | |||
: NULL; | |||
lilv_nodes_free(minimums); | |||
} | |||
if (max) { | |||
LilvNodes* maximums = lilv_port_get_value_by_node( | |||
p, port, p->world->uris.lv2_maximum); | |||
*max = maximums | |||
? lilv_node_duplicate(lilv_nodes_get_first(maximums)) | |||
: NULL; | |||
lilv_nodes_free(maximums); | |||
} | |||
} | |||
LILV_API | |||
LilvScalePoints* | |||
lilv_port_get_scale_points(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
SordIter* points = lilv_world_query_internal( | |||
p->world, | |||
port->node, | |||
sord_new_uri(p->world->world, (const uint8_t*)LV2_CORE__scalePoint), | |||
NULL); | |||
LilvScalePoints* ret = NULL; | |||
if (!sord_iter_end(points)) | |||
ret = lilv_scale_points_new(); | |||
FOREACH_MATCH(points) { | |||
const SordNode* point = sord_iter_get_node(points, SORD_OBJECT); | |||
LilvNode* value = lilv_plugin_get_unique( | |||
p, | |||
point, | |||
p->world->uris.rdf_value); | |||
LilvNode* label = lilv_plugin_get_unique( | |||
p, | |||
point, | |||
p->world->uris.rdfs_label); | |||
if (value && label) { | |||
zix_tree_insert( | |||
(ZixTree*)ret, lilv_scale_point_new(value, label), NULL); | |||
} | |||
} | |||
sord_iter_free(points); | |||
assert(!ret || lilv_nodes_size(ret) > 0); | |||
return ret; | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_port_get_properties(const LilvPlugin* p, | |||
const LilvPort* port) | |||
{ | |||
LilvNode* pred = lilv_node_new_from_node( | |||
p->world, p->world->uris.lv2_portProperty); | |||
LilvNodes* ret = lilv_port_get_value(p, port, pred); | |||
lilv_node_free(pred); | |||
return ret; | |||
} |
@@ -0,0 +1,140 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <limits.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
typedef enum { | |||
LILV_LANG_MATCH_NONE, ///< Language does not match at all | |||
LILV_LANG_MATCH_PARTIAL, ///< Partial (language, but not country) match | |||
LILV_LANG_MATCH_EXACT ///< Exact (language and country) match | |||
} LilvLangMatch; | |||
static LilvLangMatch | |||
lilv_lang_matches(const char* a, const char* b) | |||
{ | |||
if (!strcmp(a, b)) { | |||
return LILV_LANG_MATCH_EXACT; | |||
} | |||
const char* a_dash = strchr(a, '-'); | |||
const size_t a_lang_len = a_dash ? (size_t)(a_dash - a) : strlen(a); | |||
const char* b_dash = strchr(b, '-'); | |||
const size_t b_lang_len = b_dash ? (size_t)(b_dash - b) : strlen(b); | |||
if (a_lang_len == b_lang_len && !strncmp(a, b, a_lang_len)) { | |||
return LILV_LANG_MATCH_PARTIAL; | |||
} | |||
return LILV_LANG_MATCH_NONE; | |||
} | |||
static LilvNodes* | |||
lilv_nodes_from_stream_objects_i18n(LilvWorld* world, | |||
SordIter* stream, | |||
SordQuadIndex field) | |||
{ | |||
LilvNodes* values = lilv_nodes_new(); | |||
const SordNode* nolang = NULL; // Untranslated value | |||
const SordNode* partial = NULL; // Partial language match | |||
char* syslang = lilv_get_lang(); | |||
FOREACH_MATCH(stream) { | |||
const SordNode* value = sord_iter_get_node(stream, field); | |||
if (sord_node_get_type(value) == SORD_LITERAL) { | |||
const char* lang = sord_node_get_language(value); | |||
LilvLangMatch lm = LILV_LANG_MATCH_NONE; | |||
if (lang) { | |||
lm = (syslang) | |||
? lilv_lang_matches(lang, syslang) | |||
: LILV_LANG_MATCH_PARTIAL; | |||
} else { | |||
nolang = value; | |||
if (!syslang) { | |||
lm = LILV_LANG_MATCH_EXACT; | |||
} | |||
} | |||
if (lm == LILV_LANG_MATCH_EXACT) { | |||
// Exact language match, add to results | |||
zix_tree_insert((ZixTree*)values, | |||
lilv_node_new_from_node(world, value), | |||
NULL); | |||
} else if (lm == LILV_LANG_MATCH_PARTIAL) { | |||
// Partial language match, save in case we find no exact | |||
partial = value; | |||
} | |||
} else { | |||
zix_tree_insert((ZixTree*)values, | |||
lilv_node_new_from_node(world, value), | |||
NULL); | |||
} | |||
} | |||
sord_iter_free(stream); | |||
free(syslang); | |||
if (lilv_nodes_size(values) > 0) { | |||
return values; | |||
} | |||
const SordNode* best = nolang; | |||
if (syslang && partial) { | |||
// Partial language match for system language | |||
best = partial; | |||
} else if (!best) { | |||
// No languages matches at all, and no untranslated value | |||
// Use any value, if possible | |||
best = partial; | |||
} | |||
if (best) { | |||
zix_tree_insert( | |||
(ZixTree*)values, lilv_node_new_from_node(world, best), NULL); | |||
} else { | |||
// No matches whatsoever | |||
lilv_nodes_free(values); | |||
values = NULL; | |||
} | |||
return values; | |||
} | |||
LilvNodes* | |||
lilv_nodes_from_stream_objects(LilvWorld* world, | |||
SordIter* stream, | |||
SordQuadIndex field) | |||
{ | |||
if (sord_iter_end(stream)) { | |||
sord_iter_free(stream); | |||
return NULL; | |||
} else if (world->opt.filter_language) { | |||
return lilv_nodes_from_stream_objects_i18n(world, stream, field); | |||
} else { | |||
LilvNodes* values = lilv_nodes_new(); | |||
FOREACH_MATCH(stream) { | |||
const SordNode* value = sord_iter_get_node(stream, field); | |||
LilvNode* node = lilv_node_new_from_node(world, value); | |||
if (node) { | |||
zix_tree_insert((ZixTree*)values, node, NULL); | |||
} | |||
} | |||
sord_iter_free(stream); | |||
return values; | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "lilv_internal.h" | |||
/** Ownership of value and label is taken */ | |||
LilvScalePoint* | |||
lilv_scale_point_new(LilvNode* value, LilvNode* label) | |||
{ | |||
LilvScalePoint* point = (LilvScalePoint*)malloc( | |||
sizeof(struct LilvScalePointImpl)); | |||
point->value = value; | |||
point->label = label; | |||
return point; | |||
} | |||
void | |||
lilv_scale_point_free(LilvScalePoint* point) | |||
{ | |||
if (point) { | |||
lilv_node_free(point->value); | |||
lilv_node_free(point->label); | |||
free(point); | |||
} | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_scale_point_get_value(const LilvScalePoint* p) | |||
{ | |||
return p->value; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_scale_point_get_label(const LilvScalePoint* p) | |||
{ | |||
return p->label; | |||
} |
@@ -0,0 +1,184 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv_internal.h" | |||
LilvUI* | |||
lilv_ui_new(LilvWorld* world, | |||
LilvNode* uri, | |||
LilvNode* type_uri, | |||
LilvNode* binary_uri) | |||
{ | |||
assert(uri); | |||
assert(type_uri); | |||
assert(binary_uri); | |||
LilvUI* ui = (LilvUI*)malloc(sizeof(LilvUI)); | |||
ui->world = world; | |||
ui->uri = uri; | |||
ui->binary_uri = binary_uri; | |||
// FIXME: kludge | |||
char* bundle = lilv_strdup(lilv_node_as_string(ui->binary_uri)); | |||
char* last_slash = strrchr(bundle, '/') + 1; | |||
*last_slash = '\0'; | |||
ui->bundle_uri = lilv_new_uri(world, bundle); | |||
free(bundle); | |||
ui->classes = lilv_nodes_new(); | |||
zix_tree_insert((ZixTree*)ui->classes, type_uri, NULL); | |||
return ui; | |||
} | |||
void | |||
lilv_ui_free(LilvUI* ui) | |||
{ | |||
lilv_node_free(ui->uri); | |||
ui->uri = NULL; | |||
lilv_node_free(ui->bundle_uri); | |||
ui->bundle_uri = NULL; | |||
lilv_node_free(ui->binary_uri); | |||
ui->binary_uri = NULL; | |||
lilv_nodes_free(ui->classes); | |||
free(ui); | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_ui_get_uri(const LilvUI* ui) | |||
{ | |||
assert(ui); | |||
assert(ui->uri); | |||
return ui->uri; | |||
} | |||
LILV_API | |||
unsigned | |||
lilv_ui_is_supported(const LilvUI* ui, | |||
LilvUISupportedFunc supported_func, | |||
const LilvNode* container_type, | |||
const LilvNode** ui_type) | |||
{ | |||
const LilvNodes* classes = lilv_ui_get_classes(ui); | |||
LILV_FOREACH(nodes, c, classes) { | |||
const LilvNode* type = lilv_nodes_get(classes, c); | |||
const unsigned q = supported_func(lilv_node_as_uri(container_type), | |||
lilv_node_as_uri(type)); | |||
if (q) { | |||
if (ui_type) { | |||
*ui_type = type; | |||
} | |||
return q; | |||
} | |||
} | |||
return 0; | |||
} | |||
LILV_API | |||
const LilvNodes* | |||
lilv_ui_get_classes(const LilvUI* ui) | |||
{ | |||
return ui->classes; | |||
} | |||
LILV_API | |||
bool | |||
lilv_ui_is_a(const LilvUI* ui, const LilvNode* ui_class_uri) | |||
{ | |||
return lilv_nodes_contains(ui->classes, ui_class_uri); | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_ui_get_bundle_uri(const LilvUI* ui) | |||
{ | |||
assert(ui); | |||
assert(ui->bundle_uri); | |||
return ui->bundle_uri; | |||
} | |||
LILV_API | |||
const LilvNode* | |||
lilv_ui_get_binary_uri(const LilvUI* ui) | |||
{ | |||
assert(ui); | |||
assert(ui->binary_uri); | |||
return ui->binary_uri; | |||
} | |||
static LilvNodes* | |||
lilv_ui_get_value_internal(const LilvUI* ui, | |||
const SordNode* predicate) | |||
{ | |||
return lilv_world_query_values_internal( | |||
ui->world, ui->uri->node, predicate, NULL); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_supported_features(const LilvUI* ui) | |||
{ | |||
LilvNodes* optional = lilv_ui_get_optional_features(ui); | |||
LilvNodes* required = lilv_ui_get_required_features(ui); | |||
LilvNodes* result = lilv_nodes_new(); | |||
LILV_FOREACH(nodes, i, optional) | |||
zix_tree_insert((ZixTree*)result, | |||
lilv_node_duplicate(lilv_nodes_get(optional, i)), | |||
NULL); | |||
LILV_FOREACH(nodes, i, required) | |||
zix_tree_insert((ZixTree*)result, | |||
lilv_node_duplicate(lilv_nodes_get(required, i)), | |||
NULL); | |||
lilv_nodes_free(optional); | |||
lilv_nodes_free(required); | |||
return result; | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_required_features(const LilvUI* ui) | |||
{ | |||
return lilv_ui_get_value_internal( | |||
ui, ui->world->uris.lv2_requiredFeature); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_optional_features(const LilvUI* ui) | |||
{ | |||
return lilv_ui_get_value_internal( | |||
ui, ui->world->uris.lv2_optionalFeature); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_ui_get_extension_data(const LilvUI* ui) | |||
{ | |||
return lilv_ui_get_value_internal(ui, ui->world->uris.lv2_extensionData); | |||
} |
@@ -0,0 +1,604 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#define _POSIX_C_SOURCE 1 /* for fileno */ | |||
#define _BSD_SOURCE 1 /* for realpath, symlink */ | |||
#ifdef __APPLE__ | |||
# define _DARWIN_C_SOURCE 1 /* for flock */ | |||
#endif | |||
#include <assert.h> | |||
#include <ctype.h> | |||
#include <errno.h> | |||
#include <stdarg.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#ifdef _WIN32 | |||
# include <windows.h> | |||
# include <direct.h> | |||
# include <io.h> | |||
# define F_OK 0 | |||
# define mkdir(path, flags) _mkdir(path) | |||
#else | |||
# include <dirent.h> | |||
# include <unistd.h> | |||
#endif | |||
#include <sys/stat.h> | |||
#include <sys/types.h> | |||
#include "lilv_internal.h" | |||
#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) | |||
# include <sys/file.h> | |||
#endif | |||
#ifndef PAGE_SIZE | |||
# define PAGE_SIZE 4096 | |||
#endif | |||
char* | |||
lilv_strjoin(const char* first, ...) | |||
{ | |||
size_t len = strlen(first); | |||
char* result = (char*)malloc(len + 1); | |||
memcpy(result, first, len); | |||
va_list args; | |||
va_start(args, first); | |||
while (1) { | |||
const char* const s = va_arg(args, const char *); | |||
if (s == NULL) | |||
break; | |||
const size_t this_len = strlen(s); | |||
if (!(result = (char*)realloc(result, len + this_len + 1))) { | |||
free(result); | |||
LILV_ERROR("realloc() failed\n"); | |||
return NULL; | |||
} | |||
memcpy(result + len, s, this_len); | |||
len += this_len; | |||
} | |||
va_end(args); | |||
result[len] = '\0'; | |||
return result; | |||
} | |||
char* | |||
lilv_strdup(const char* str) | |||
{ | |||
if (!str) { | |||
return NULL; | |||
} | |||
const size_t len = strlen(str); | |||
char* dup = (char*)malloc(len + 1); | |||
memcpy(dup, str, len + 1); | |||
return dup; | |||
} | |||
const char* | |||
lilv_uri_to_path(const char* uri) | |||
{ | |||
return (const char*)serd_uri_to_path((const uint8_t*)uri); | |||
} | |||
/** Return the current LANG converted to Turtle (i.e. RFC3066) style. | |||
* For example, if LANG is set to "en_CA.utf-8", this returns "en-ca". | |||
*/ | |||
char* | |||
lilv_get_lang(void) | |||
{ | |||
const char* const env_lang = getenv("LANG"); | |||
if (!env_lang || !strcmp(env_lang, "") | |||
|| !strcmp(env_lang, "C") || !strcmp(env_lang, "POSIX")) { | |||
return NULL; | |||
} | |||
const size_t env_lang_len = strlen(env_lang); | |||
char* const lang = (char*)malloc(env_lang_len + 1); | |||
for (size_t i = 0; i < env_lang_len + 1; ++i) { | |||
if (env_lang[i] == '_') { | |||
lang[i] = '-'; // Convert _ to - | |||
} else if (env_lang[i] >= 'A' && env_lang[i] <= 'Z') { | |||
lang[i] = env_lang[i] + ('a' - 'A'); // Convert to lowercase | |||
} else if (env_lang[i] >= 'a' && env_lang[i] <= 'z') { | |||
lang[i] = env_lang[i]; // Lowercase letter, copy verbatim | |||
} else if (env_lang[i] >= '0' && env_lang[i] <= '9') { | |||
lang[i] = env_lang[i]; // Digit, copy verbatim | |||
} else if (env_lang[i] == '\0' || env_lang[i] == '.') { | |||
// End, or start of suffix (e.g. en_CA.utf-8), finished | |||
lang[i] = '\0'; | |||
break; | |||
} else { | |||
LILV_ERRORF("Illegal LANG `%s' ignored\n", env_lang); | |||
free(lang); | |||
return NULL; | |||
} | |||
} | |||
return lang; | |||
} | |||
/** Append suffix to dst, update dst_len, and return the realloc'd result. */ | |||
static char* | |||
strappend(char* dst, size_t* dst_len, const char* suffix, size_t suffix_len) | |||
{ | |||
dst = (char*)realloc(dst, *dst_len + suffix_len + 1); | |||
memcpy(dst + *dst_len, suffix, suffix_len); | |||
dst[(*dst_len += suffix_len)] = '\0'; | |||
return dst; | |||
} | |||
/** Append the value of the environment variable var to dst. */ | |||
static char* | |||
append_var(char* dst, size_t* dst_len, const char* var) | |||
{ | |||
// Get value from environment | |||
const char* val = getenv(var); | |||
if (val) { // Value found, append it | |||
return strappend(dst, dst_len, val, strlen(val)); | |||
} else { // No value found, append variable reference as-is | |||
return strappend(strappend(dst, dst_len, "$", 1), | |||
dst_len, var, strlen(var)); | |||
} | |||
} | |||
/** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */ | |||
char* | |||
lilv_expand(const char* path) | |||
{ | |||
#ifdef _WIN32 | |||
char* out = (char*)malloc(MAX_PATH); | |||
ExpandEnvironmentStrings(path, out, MAX_PATH); | |||
#else | |||
char* out = NULL; | |||
size_t len = 0; | |||
const char* start = path; // Start of current chunk to copy | |||
for (const char* s = path; *s;) { | |||
if (*s == '$') { | |||
// Hit $ (variable reference, e.g. $VAR_NAME) | |||
for (const char* t = s + 1; ; ++t) { | |||
if (!*t || (!isupper(*t) && !isdigit(*t) && *t != '_')) { | |||
// Append preceding chunk | |||
out = strappend(out, &len, start, s - start); | |||
// Append variable value (or $VAR_NAME if not found) | |||
char* var = (char*)calloc(t - s, 1); | |||
memcpy(var, s + 1, t - s - 1); | |||
out = append_var(out, &len, var); | |||
free(var); | |||
// Continue after variable reference | |||
start = s = t; | |||
break; | |||
} | |||
} | |||
} else if (*s == '~' && (*(s + 1) == '/' || !*(s + 1))) { | |||
// Hit ~ before slash or end of string (home directory reference) | |||
out = strappend(out, &len, start, s - start); | |||
out = append_var(out, &len, "HOME"); | |||
start = ++s; | |||
} else { | |||
++s; | |||
} | |||
} | |||
if (*start) { | |||
out = strappend(out, &len, start, strlen(start)); | |||
} | |||
#endif | |||
return out; | |||
} | |||
static bool | |||
lilv_is_dir_sep(const char c) | |||
{ | |||
return c == '/' || c == LILV_DIR_SEP[0]; | |||
} | |||
char* | |||
lilv_dirname(const char* path) | |||
{ | |||
const char* s = path + strlen(path) - 1; // Last character | |||
for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash | |||
for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash | |||
for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates | |||
if (s == path) { // Hit beginning | |||
return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); | |||
} else { // Pointing to the last character of the result (inclusive) | |||
char* dirname = (char*)malloc(s - path + 2); | |||
memcpy(dirname, path, s - path + 1); | |||
dirname[s - path + 1] = '\0'; | |||
return dirname; | |||
} | |||
} | |||
bool | |||
lilv_path_exists(const char* path, void* ignored) | |||
{ | |||
return !access(path, F_OK); | |||
} | |||
char* | |||
lilv_find_free_path(const char* in_path, | |||
bool (*exists)(const char*, void*), void* user_data) | |||
{ | |||
const size_t in_path_len = strlen(in_path); | |||
char* path = (char*)malloc(in_path_len + 7); | |||
memcpy(path, in_path, in_path_len + 1); | |||
for (int i = 2; i < 1000000; ++i) { | |||
if (!exists(path, user_data)) { | |||
return path; | |||
} | |||
snprintf(path, in_path_len + 7, "%s.%u", in_path, i); | |||
} | |||
return NULL; | |||
} | |||
int | |||
lilv_copy_file(const char* src, const char* dst) | |||
{ | |||
FILE* in = fopen(src, "r"); | |||
if (!in) { | |||
LILV_ERRORF("error opening %s (%s)\n", src, strerror(errno)); | |||
return 1; | |||
} | |||
FILE* out = fopen(dst, "w"); | |||
if (!out) { | |||
LILV_ERRORF("error opening %s (%s)\n", dst, strerror(errno)); | |||
fclose(in); | |||
return 2; | |||
} | |||
char* page = (char*)malloc(PAGE_SIZE); | |||
size_t n_read = 0; | |||
while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) { | |||
if (fwrite(page, 1, n_read, out) != n_read) { | |||
LILV_ERRORF("write to %s failed (%s)\n", dst, strerror(errno)); | |||
break; | |||
} | |||
} | |||
const int ret = ferror(in) || ferror(out); | |||
if (ferror(in)) { | |||
LILV_ERRORF("read from %s failed (%s)\n", src, strerror(errno)); | |||
} | |||
free(page); | |||
fclose(in); | |||
fclose(out); | |||
return ret; | |||
} | |||
bool | |||
lilv_path_is_absolute(const char* path) | |||
{ | |||
if (lilv_is_dir_sep(path[0])) { | |||
return true; | |||
} | |||
#ifdef _WIN32 | |||
if (isalpha(path[0]) && path[1] == ':' && lilv_is_dir_sep(path[2])) { | |||
return true; | |||
} | |||
#endif | |||
return false; | |||
} | |||
char* | |||
lilv_path_absolute(const char* path) | |||
{ | |||
if (lilv_path_is_absolute(path)) { | |||
return lilv_strdup(path); | |||
} else { | |||
char* cwd = getcwd(NULL, 0); | |||
char* abs_path = lilv_path_join(cwd, path); | |||
free(cwd); | |||
return abs_path; | |||
} | |||
} | |||
char* | |||
lilv_path_join(const char* a, const char* b) | |||
{ | |||
if (!a) { | |||
return lilv_strdup(b); | |||
} | |||
const size_t a_len = strlen(a); | |||
const size_t b_len = b ? strlen(b) : 0; | |||
const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0); | |||
char* path = (char*)calloc(1, a_len + b_len + 2); | |||
memcpy(path, a, pre_len); | |||
path[pre_len] = '/'; | |||
if (b) { | |||
memcpy(path + pre_len + 1, | |||
b + (lilv_is_dir_sep(b[0]) ? 1 : 0), | |||
lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len); | |||
} | |||
return path; | |||
} | |||
static void | |||
lilv_size_mtime(const char* path, off_t* size, time_t* time) | |||
{ | |||
struct stat buf; | |||
if (stat(path, &buf)) { | |||
LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
} | |||
if (size) { | |||
*size = buf.st_size; | |||
} | |||
if (time) { | |||
*time = buf.st_mtime; | |||
} | |||
} | |||
typedef struct { | |||
char* pattern; | |||
off_t orig_size; | |||
time_t time; | |||
char* latest; | |||
} Latest; | |||
static void | |||
update_latest(const char* path, const char* name, void* data) | |||
{ | |||
Latest* latest = (Latest*)data; | |||
char* entry_path = lilv_path_join(path, name); | |||
unsigned num; | |||
if (sscanf(entry_path, latest->pattern, &num) == 1) { | |||
off_t entry_size = 0; | |||
time_t entry_time = 0; | |||
lilv_size_mtime(entry_path, &entry_size, &entry_time); | |||
if (entry_size == latest->orig_size && entry_time >= latest->time) { | |||
free(latest->latest); | |||
latest->latest = entry_path; | |||
} | |||
} | |||
if (entry_path != latest->latest) { | |||
free(entry_path); | |||
} | |||
} | |||
/** Return the latest copy of the file at @c path that is newer. */ | |||
char* | |||
lilv_get_latest_copy(const char* path, const char* copy_path) | |||
{ | |||
char* copy_dir = lilv_dirname(copy_path); | |||
Latest latest = { lilv_strjoin(copy_path, "%u", NULL), 0, 0, NULL }; | |||
lilv_size_mtime(path, &latest.orig_size, &latest.time); | |||
lilv_dir_for_each(copy_dir, &latest, update_latest); | |||
free(latest.pattern); | |||
free(copy_dir); | |||
return latest.latest; | |||
} | |||
char* | |||
lilv_realpath(const char* path) | |||
{ | |||
#ifdef _WIN32 | |||
char* out = (char*)malloc(MAX_PATH); | |||
GetFullPathName(path, MAX_PATH, out, NULL); | |||
return out; | |||
#else | |||
char* real_path = realpath(path, NULL); | |||
return real_path ? real_path : lilv_strdup(path); | |||
#endif | |||
} | |||
int | |||
lilv_symlink(const char* oldpath, const char* newpath) | |||
{ | |||
int ret = 0; | |||
if (strcmp(oldpath, newpath)) { | |||
#ifdef _WIN32 | |||
ret = 0; | |||
#else | |||
ret = symlink(oldpath, newpath); | |||
#endif | |||
} | |||
if (ret) { | |||
LILV_ERRORF("Failed to link %s => %s (%s)\n", | |||
newpath, oldpath, strerror(errno)); | |||
} | |||
return ret; | |||
} | |||
char* | |||
lilv_path_relative_to(const char* path, const char* base) | |||
{ | |||
const size_t path_len = strlen(path); | |||
const size_t base_len = strlen(base); | |||
const size_t min_len = (path_len < base_len) ? path_len : base_len; | |||
// Find the last separator common to both paths | |||
size_t last_shared_sep = 0; | |||
for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { | |||
if (lilv_is_dir_sep(path[i])) { | |||
last_shared_sep = i; | |||
} | |||
} | |||
if (last_shared_sep == 0) { | |||
// No common components, return path | |||
return lilv_strdup(path); | |||
} | |||
// Find the number of up references ("..") required | |||
size_t up = 0; | |||
for (size_t i = last_shared_sep + 1; i < base_len; ++i) { | |||
if (lilv_is_dir_sep(base[i])) { | |||
++up; | |||
} | |||
} | |||
// Write up references | |||
const size_t suffix_len = path_len - last_shared_sep; | |||
char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1); | |||
for (size_t i = 0; i < up; ++i) { | |||
memcpy(rel + (i * 3), "../", 3); | |||
} | |||
// Write suffix | |||
memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); | |||
return rel; | |||
} | |||
bool | |||
lilv_path_is_child(const char* path, const char* dir) | |||
{ | |||
if (path && dir) { | |||
const size_t path_len = strlen(path); | |||
const size_t dir_len = strlen(dir); | |||
return dir && path_len >= dir_len && !strncmp(path, dir, dir_len); | |||
} | |||
return false; | |||
} | |||
int | |||
lilv_flock(FILE* file, bool lock) | |||
{ | |||
#if defined(HAVE_FLOCK) && defined(HAVE_FILENO) | |||
return flock(fileno(file), lock ? LOCK_EX : LOCK_UN); | |||
#else | |||
return 0; | |||
#endif | |||
} | |||
void | |||
lilv_dir_for_each(const char* path, | |||
void* data, | |||
void (*f)(const char* path, const char* name, void* data)) | |||
{ | |||
#ifdef _WIN32 | |||
char* pat = lilv_path_join(path, "*"); | |||
WIN32_FIND_DATA fd; | |||
HANDLE fh = FindFirstFile(pat, &fd); | |||
if (fh != INVALID_HANDLE_VALUE) { | |||
do { | |||
f(path, fd.cFileName, data); | |||
} while (FindNextFile(fh, &fd)); | |||
} | |||
free(pat); | |||
#else | |||
DIR* dir = opendir(path); | |||
if (dir) { | |||
struct dirent entry; | |||
struct dirent* result; | |||
while (!readdir_r(dir, &entry, &result) && result) { | |||
f(path, entry.d_name, data); | |||
} | |||
closedir(dir); | |||
} | |||
#endif | |||
} | |||
int | |||
lilv_mkdir_p(const char* dir_path) | |||
{ | |||
char* path = lilv_strdup(dir_path); | |||
const size_t path_len = strlen(path); | |||
for (size_t i = 1; i <= path_len; ++i) { | |||
if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') { | |||
path[i] = '\0'; | |||
if (mkdir(path, 0755) && errno != EEXIST) { | |||
LILV_ERRORF("Failed to create %s (%s)\n", | |||
path, strerror(errno)); | |||
free(path); | |||
return 1; | |||
} | |||
path[i] = LILV_DIR_SEP[0]; | |||
} | |||
} | |||
free(path); | |||
return 0; | |||
} | |||
static off_t | |||
lilv_file_size(const char* path) | |||
{ | |||
struct stat buf; | |||
if (stat(path, &buf)) { | |||
LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
return 0; | |||
} | |||
return buf.st_size; | |||
} | |||
bool | |||
lilv_file_equals(const char* a_path, const char* b_path) | |||
{ | |||
if (!strcmp(a_path, b_path)) { | |||
return true; // Paths match | |||
} | |||
bool match = false; | |||
FILE* a_file = NULL; | |||
FILE* b_file = NULL; | |||
char* const a_real = lilv_realpath(a_path); | |||
char* const b_real = lilv_realpath(b_path); | |||
if (!a_real || !b_real) { | |||
match = false; // Missing file matches nothing | |||
} else if (!strcmp(a_real, b_real)) { | |||
match = true; // Real paths match | |||
} else if (lilv_file_size(a_path) != lilv_file_size(b_path)) { | |||
match = false; // Sizes differ | |||
} else if (!(a_file = fopen(a_real, "rb"))) { | |||
match = false; // Missing file matches nothing | |||
} else if (!(b_file = fopen(b_real, "rb"))) { | |||
match = false; // Missing file matches nothing | |||
} else { | |||
match = true; | |||
// TODO: Improve performance by reading chunks | |||
while (!feof(a_file) && !feof(b_file)) { | |||
if (fgetc(a_file) != fgetc(b_file)) { | |||
match = false; | |||
break; | |||
} | |||
} | |||
} | |||
if (a_file) { | |||
fclose(a_file); | |||
} | |||
if (b_file) { | |||
fclose(b_file); | |||
} | |||
free(a_real); | |||
free(b_real); | |||
return match; | |||
} |
@@ -0,0 +1,822 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <errno.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/presets.h" | |||
#include "lilv_internal.h" | |||
LILV_API | |||
LilvWorld* | |||
lilv_world_new(void) | |||
{ | |||
LilvWorld* world = (LilvWorld*)malloc(sizeof(LilvWorld)); | |||
world->world = sord_world_new(); | |||
if (!world->world) | |||
goto fail; | |||
world->model = sord_new(world->world, SORD_SPO|SORD_OPS, true); | |||
if (!world->model) | |||
goto fail; | |||
world->specs = NULL; | |||
world->plugin_classes = lilv_plugin_classes_new(); | |||
world->plugins = lilv_plugins_new(); | |||
world->loaded_files = zix_tree_new( | |||
false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free); | |||
#ifdef LILV_NEW_LV2 | |||
world->libs = zix_tree_new( | |||
false, lilv_header_compare_by_uri, NULL, NULL); | |||
#endif | |||
#define NS_DCTERMS "http://purl.org/dc/terms/" | |||
#define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#" | |||
#define NEW_URI(uri) sord_new_uri(world->world, (const uint8_t*)uri) | |||
world->uris.dc_replaces = NEW_URI(NS_DCTERMS "replaces"); | |||
world->uris.dman_DynManifest = NEW_URI(NS_DYNMAN "DynManifest"); | |||
world->uris.doap_name = NEW_URI(LILV_NS_DOAP "name"); | |||
world->uris.lv2_Plugin = NEW_URI(LV2_CORE__Plugin); | |||
world->uris.lv2_Specification = NEW_URI(LV2_CORE__Specification); | |||
world->uris.lv2_appliesTo = NEW_URI(LV2_CORE__appliesTo); | |||
world->uris.lv2_binary = NEW_URI(LV2_CORE__binary); | |||
world->uris.lv2_default = NEW_URI(LV2_CORE__default); | |||
world->uris.lv2_designation = NEW_URI(LV2_CORE__designation); | |||
world->uris.lv2_extensionData = NEW_URI(LV2_CORE__extensionData); | |||
world->uris.lv2_index = NEW_URI(LV2_CORE__index); | |||
world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum); | |||
world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum); | |||
world->uris.lv2_name = NEW_URI(LV2_CORE__name); | |||
world->uris.lv2_optionalFeature = NEW_URI(LV2_CORE__optionalFeature); | |||
world->uris.lv2_port = NEW_URI(LV2_CORE__port); | |||
world->uris.lv2_portProperty = NEW_URI(LV2_CORE__portProperty); | |||
world->uris.lv2_reportsLatency = NEW_URI(LV2_CORE__reportsLatency); | |||
world->uris.lv2_requiredFeature = NEW_URI(LV2_CORE__requiredFeature); | |||
world->uris.lv2_symbol = NEW_URI(LV2_CORE__symbol); | |||
world->uris.pset_value = NEW_URI(LV2_PRESETS__value); | |||
world->uris.rdf_a = NEW_URI(LILV_NS_RDF "type"); | |||
world->uris.rdf_value = NEW_URI(LILV_NS_RDF "value"); | |||
world->uris.rdfs_Class = NEW_URI(LILV_NS_RDFS "Class"); | |||
world->uris.rdfs_label = NEW_URI(LILV_NS_RDFS "label"); | |||
world->uris.rdfs_seeAlso = NEW_URI(LILV_NS_RDFS "seeAlso"); | |||
world->uris.rdfs_subClassOf = NEW_URI(LILV_NS_RDFS "subClassOf"); | |||
world->uris.xsd_base64Binary = NEW_URI(LILV_NS_XSD "base64Binary"); | |||
world->uris.xsd_boolean = NEW_URI(LILV_NS_XSD "boolean"); | |||
world->uris.xsd_decimal = NEW_URI(LILV_NS_XSD "decimal"); | |||
world->uris.xsd_double = NEW_URI(LILV_NS_XSD "double"); | |||
world->uris.xsd_integer = NEW_URI(LILV_NS_XSD "integer"); | |||
world->uris.null_uri = NULL; | |||
world->lv2_plugin_class = lilv_plugin_class_new( | |||
world, NULL, world->uris.lv2_Plugin, "Plugin"); | |||
assert(world->lv2_plugin_class); | |||
world->n_read_files = 0; | |||
world->opt.filter_language = true; | |||
world->opt.dyn_manifest = true; | |||
return world; | |||
fail: | |||
/* keep on rockin' in the */ free(world); | |||
return NULL; | |||
} | |||
LILV_API | |||
void | |||
lilv_world_free(LilvWorld* world) | |||
{ | |||
if (!world) { | |||
return; | |||
} | |||
lilv_plugin_class_free(world->lv2_plugin_class); | |||
world->lv2_plugin_class = NULL; | |||
for (SordNode** n = (SordNode**)&world->uris; *n; ++n) { | |||
sord_node_free(world->world, *n); | |||
} | |||
for (LilvSpec* spec = world->specs; spec;) { | |||
LilvSpec* next = spec->next; | |||
sord_node_free(world->world, spec->spec); | |||
sord_node_free(world->world, spec->bundle); | |||
lilv_nodes_free(spec->data_uris); | |||
free(spec); | |||
spec = next; | |||
} | |||
world->specs = NULL; | |||
LILV_FOREACH(plugins, i, world->plugins) { | |||
const LilvPlugin* p = lilv_plugins_get(world->plugins, i); | |||
lilv_plugin_free((LilvPlugin*)p); | |||
} | |||
zix_tree_free((ZixTree*)world->plugins); | |||
world->plugins = NULL; | |||
zix_tree_free((ZixTree*)world->loaded_files); | |||
world->loaded_files = NULL; | |||
#ifdef LILV_NEW_LV2 | |||
zix_tree_free((ZixTree*)world->libs); | |||
world->libs = NULL; | |||
#endif | |||
zix_tree_free((ZixTree*)world->plugin_classes); | |||
world->plugin_classes = NULL; | |||
sord_free(world->model); | |||
world->model = NULL; | |||
sord_world_free(world->world); | |||
world->world = NULL; | |||
free(world); | |||
} | |||
LILV_API | |||
void | |||
lilv_world_set_option(LilvWorld* world, | |||
const char* option, | |||
const LilvNode* value) | |||
{ | |||
if (!strcmp(option, LILV_OPTION_DYN_MANIFEST)) { | |||
if (lilv_node_is_bool(value)) { | |||
world->opt.dyn_manifest = lilv_node_as_bool(value); | |||
return; | |||
} | |||
} else if (!strcmp(option, LILV_OPTION_FILTER_LANG)) { | |||
if (lilv_node_is_bool(value)) { | |||
world->opt.filter_language = lilv_node_as_bool(value); | |||
return; | |||
} | |||
} | |||
LILV_WARNF("Unrecognized or invalid option `%s'\n", option); | |||
} | |||
LILV_API | |||
LilvNodes* | |||
lilv_world_find_nodes(LilvWorld* world, | |||
const LilvNode* subject, | |||
const LilvNode* predicate, | |||
const LilvNode* object) | |||
{ | |||
if (subject && !lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) { | |||
LILV_ERRORF("Subject `%s' is not a resource\n", | |||
sord_node_get_string(subject->node)); | |||
return NULL; | |||
} | |||
if (!lilv_node_is_uri(predicate)) { | |||
LILV_ERRORF("Predicate `%s' is not a URI\n", | |||
sord_node_get_string(predicate->node)); | |||
return NULL; | |||
} | |||
if (!subject && !object) { | |||
LILV_ERROR("Both subject and object are NULL\n"); | |||
return NULL; | |||
} | |||
SordNode* const subject_node = subject | |||
? sord_node_copy(subject->node) | |||
: NULL; | |||
SordNode* const object_node = object | |||
? sord_node_copy(object->node) | |||
: NULL; | |||
LilvNodes* ret = lilv_world_query_values_internal( | |||
world, subject_node, predicate->node, object_node); | |||
sord_node_free(world->world, subject_node); | |||
sord_node_free(world->world, object_node); | |||
return ret; | |||
} | |||
SordIter* | |||
lilv_world_query_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object) | |||
{ | |||
return sord_search(world->model, subject, predicate, object, NULL); | |||
} | |||
LilvNodes* | |||
lilv_world_query_values_internal(LilvWorld* world, | |||
const SordNode* subject, | |||
const SordNode* predicate, | |||
const SordNode* object) | |||
{ | |||
return lilv_nodes_from_stream_objects( | |||
world, | |||
lilv_world_query_internal(world, subject, predicate, object), | |||
(object == NULL) ? SORD_OBJECT : SORD_SUBJECT); | |||
} | |||
static SerdNode | |||
lilv_new_uri_relative_to_base(const uint8_t* uri_str, | |||
const uint8_t* base_uri_str) | |||
{ | |||
SerdURI base_uri; | |||
if (serd_uri_parse(base_uri_str, &base_uri)) { | |||
return SERD_NODE_NULL; | |||
} | |||
SerdURI ignored; | |||
return serd_node_new_uri_from_string(uri_str, &base_uri, &ignored); | |||
} | |||
const uint8_t* | |||
lilv_world_blank_node_prefix(LilvWorld* world) | |||
{ | |||
static char str[32]; | |||
snprintf(str, sizeof(str), "%d", world->n_read_files++); | |||
return (const uint8_t*)str; | |||
} | |||
/** Comparator for sequences (e.g. world->plugins). */ | |||
int | |||
lilv_header_compare_by_uri(const void* a, const void* b, void* user_data) | |||
{ | |||
const struct LilvHeader* const header_a = (const struct LilvHeader*)a; | |||
const struct LilvHeader* const header_b = (const struct LilvHeader*)b; | |||
return strcmp(lilv_node_as_uri(header_a->uri), | |||
lilv_node_as_uri(header_b->uri)); | |||
} | |||
/** Get an element of a collection of any object with an LilvHeader by URI. */ | |||
struct LilvHeader* | |||
lilv_collection_get_by_uri(const ZixTree* const_seq, | |||
const LilvNode* uri) | |||
{ | |||
if (!lilv_node_is_uri(uri)) { | |||
return NULL; | |||
} | |||
ZixTree* seq = (ZixTree*)const_seq; | |||
struct LilvHeader key = { NULL, (LilvNode*)uri }; | |||
ZixTreeIter* i = NULL; | |||
ZixStatus st = zix_tree_find(seq, &key, &i); | |||
if (!st) { | |||
return (struct LilvHeader*)zix_tree_get(i); | |||
} | |||
return NULL; | |||
} | |||
static void | |||
lilv_world_add_spec(LilvWorld* world, | |||
const SordNode* specification_node, | |||
const SordNode* bundle_node) | |||
{ | |||
LilvSpec* spec = (LilvSpec*)malloc(sizeof(LilvSpec)); | |||
spec->spec = sord_node_copy(specification_node); | |||
spec->bundle = sord_node_copy(bundle_node); | |||
spec->data_uris = lilv_nodes_new(); | |||
// Add all plugin data files (rdfs:seeAlso) | |||
SordIter* files = sord_search( | |||
world->model, | |||
specification_node, | |||
world->uris.rdfs_seeAlso, | |||
NULL, | |||
NULL); | |||
FOREACH_MATCH(files) { | |||
const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT); | |||
zix_tree_insert((ZixTree*)spec->data_uris, | |||
lilv_node_new_from_node(world, file_node), | |||
NULL); | |||
} | |||
sord_iter_free(files); | |||
// Add specification to world specification list | |||
spec->next = world->specs; | |||
world->specs = spec; | |||
} | |||
static void | |||
lilv_world_add_plugin(LilvWorld* world, | |||
const SordNode* plugin_node, | |||
SerdNode* manifest_uri, | |||
void* dynmanifest, | |||
const SordNode* bundle_node) | |||
{ | |||
LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node); | |||
const LilvPlugin* last = lilv_plugins_get_by_uri(world->plugins, | |||
plugin_uri); | |||
if (last) { | |||
LILV_ERRORF("Duplicate plugin <%s>\n", lilv_node_as_uri(plugin_uri)); | |||
LILV_ERRORF("... found in %s\n", lilv_node_as_string( | |||
lilv_plugin_get_bundle_uri(last))); | |||
LILV_ERRORF("... and %s\n", sord_node_get_string(bundle_node)); | |||
lilv_node_free(plugin_uri); | |||
return; | |||
} | |||
// Create LilvPlugin | |||
LilvNode* bundle_uri = lilv_node_new_from_node(world, bundle_node); | |||
LilvPlugin* plugin = lilv_plugin_new(world, plugin_uri, bundle_uri); | |||
// Add manifest as plugin data file (as if it were rdfs:seeAlso) | |||
zix_tree_insert((ZixTree*)plugin->data_uris, | |||
lilv_new_uri(world, (const char*)manifest_uri->buf), | |||
NULL); | |||
#ifdef LILV_DYN_MANIFEST | |||
// Set dynamic manifest library URI, if applicable | |||
if (dynmanifest) { | |||
plugin->dynmanifest = (LilvDynManifest*)dynmanifest; | |||
++((LilvDynManifest*)dynmanifest)->refs; | |||
} | |||
#endif | |||
// Add all plugin data files (rdfs:seeAlso) | |||
SordIter* files = sord_search( | |||
world->model, | |||
plugin_node, | |||
world->uris.rdfs_seeAlso, | |||
NULL, | |||
NULL); | |||
FOREACH_MATCH(files) { | |||
const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT); | |||
zix_tree_insert((ZixTree*)plugin->data_uris, | |||
lilv_node_new_from_node(world, file_node), | |||
NULL); | |||
} | |||
sord_iter_free(files); | |||
// Add plugin to world plugin sequence | |||
zix_tree_insert((ZixTree*)world->plugins, plugin, NULL); | |||
} | |||
static void | |||
lilv_world_load_dyn_manifest(LilvWorld* world, | |||
SordNode* bundle_node, | |||
SerdNode manifest_uri) | |||
{ | |||
#ifdef LILV_DYN_MANIFEST | |||
if (!world->opt.dyn_manifest) { | |||
return; | |||
} | |||
typedef void* LV2_Dyn_Manifest_Handle; | |||
LV2_Dyn_Manifest_Handle handle = NULL; | |||
// ?dman a dynman:DynManifest | |||
SordIter* dmanifests = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.dman_DynManifest, | |||
bundle_node); | |||
FOREACH_MATCH(dmanifests) { | |||
const SordNode* dmanifest = sord_iter_get_node(dmanifests, SORD_SUBJECT); | |||
// ?dman lv2:binary ?binary | |||
SordIter* binaries = sord_search( | |||
world->model, | |||
dmanifest, | |||
world->uris.lv2_binary, | |||
NULL, | |||
bundle_node); | |||
if (sord_iter_end(binaries)) { | |||
sord_iter_free(binaries); | |||
LILV_ERRORF("Dynamic manifest in <%s> has no binaries, ignored\n", | |||
sord_node_get_string(bundle_node)); | |||
continue; | |||
} | |||
// Get binary path | |||
const SordNode* binary = sord_iter_get_node(binaries, SORD_OBJECT); | |||
const uint8_t* lib_uri = sord_node_get_string(binary); | |||
const char* lib_path = lilv_uri_to_path((const char*)lib_uri); | |||
if (!lib_path) { | |||
LILV_ERROR("No dynamic manifest library path\n"); | |||
sord_iter_free(binaries); | |||
continue; | |||
} | |||
// Open library | |||
void* lib = dlopen(lib_path, RTLD_LAZY); | |||
if (!lib) { | |||
LILV_ERRORF("Failed to open dynmanifest library `%s'\n", lib_path); | |||
sord_iter_free(binaries); | |||
continue; | |||
} | |||
// Open dynamic manifest | |||
typedef int (*OpenFunc)(LV2_Dyn_Manifest_Handle*, | |||
const LV2_Feature *const *); | |||
OpenFunc dmopen = (OpenFunc)lilv_dlfunc(lib, "lv2_dyn_manifest_open"); | |||
if (!dmopen || dmopen(&handle, &dman_features)) { | |||
LILV_ERRORF("No `lv2_dyn_manifest_open' in `%s'\n", lib_path); | |||
sord_iter_free(binaries); | |||
dlclose(lib); | |||
continue; | |||
} | |||
// Get subjects (the data that would be in manifest.ttl) | |||
typedef int (*GetSubjectsFunc)(LV2_Dyn_Manifest_Handle, FILE*); | |||
GetSubjectsFunc get_subjects_func = (GetSubjectsFunc)lilv_dlfunc( | |||
lib, "lv2_dyn_manifest_get_subjects"); | |||
if (!get_subjects_func) { | |||
LILV_ERRORF("No `lv2_dyn_manifest_get_subjects' in `%s'\n", | |||
lib_path); | |||
sord_iter_free(binaries); | |||
dlclose(lib); | |||
continue; | |||
} | |||
LilvDynManifest* desc = malloc(sizeof(LilvDynManifest)); | |||
desc->bundle = lilv_node_new_from_node(world, bundle_node); | |||
desc->lib = lib; | |||
desc->handle = handle; | |||
desc->refs = 0; | |||
// Generate data file | |||
FILE* fd = tmpfile(); | |||
get_subjects_func(handle, fd); | |||
rewind(fd); | |||
// Parse generated data file | |||
const SerdNode* base = sord_node_to_serd_node(dmanifest); | |||
SerdEnv* env = serd_env_new(base); | |||
SerdReader* reader = sord_new_reader( | |||
world->model, env, SERD_TURTLE, sord_node_copy(dmanifest)); | |||
serd_reader_add_blank_prefix(reader, | |||
lilv_world_blank_node_prefix(world)); | |||
serd_reader_read_file_handle(reader, fd, | |||
(const uint8_t*)"(dyn-manifest)"); | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
// Close (and automatically delete) temporary data file | |||
fclose(fd); | |||
// ?plugin a lv2:Plugin | |||
SordIter* plug_results = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.lv2_Plugin, | |||
dmanifest); | |||
FOREACH_MATCH(plug_results) { | |||
const SordNode* plugin_node = sord_iter_get_node(plug_results, SORD_SUBJECT); | |||
lilv_world_add_plugin(world, plugin_node, | |||
&manifest_uri, desc, bundle_node); | |||
} | |||
sord_iter_free(plug_results); | |||
sord_iter_free(binaries); | |||
} | |||
sord_iter_free(dmanifests); | |||
#endif // LILV_DYN_MANIFEST | |||
} | |||
LILV_API | |||
void | |||
lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) | |||
{ | |||
if (!lilv_node_is_uri(bundle_uri)) { | |||
LILV_ERRORF("Bundle URI `%s' is not a URI\n", | |||
sord_node_get_string(bundle_uri->node)); | |||
return; | |||
} | |||
SordNode* bundle_node = bundle_uri->node; | |||
SerdNode manifest_uri = lilv_new_uri_relative_to_base( | |||
(const uint8_t*)"manifest.ttl", | |||
(const uint8_t*)sord_node_get_string(bundle_node)); | |||
SerdEnv* env = serd_env_new(&manifest_uri); | |||
SerdReader* reader = sord_new_reader(world->model, env, SERD_TURTLE, | |||
bundle_node); | |||
serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world)); | |||
SerdStatus st = serd_reader_read_file(reader, manifest_uri.buf); | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
if (st) { | |||
LILV_ERRORF("Error reading %s\n", manifest_uri.buf); | |||
return; | |||
} | |||
// ?plugin a lv2:Plugin | |||
SordIter* plug_results = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.lv2_Plugin, | |||
bundle_node); | |||
FOREACH_MATCH(plug_results) { | |||
const SordNode* plugin_node = sord_iter_get_node(plug_results, SORD_SUBJECT); | |||
lilv_world_add_plugin(world, plugin_node, | |||
&manifest_uri, NULL, bundle_node); | |||
} | |||
sord_iter_free(plug_results); | |||
lilv_world_load_dyn_manifest(world, bundle_node, manifest_uri); | |||
// ?specification a lv2:Specification | |||
SordIter* spec_results = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.lv2_Specification, | |||
bundle_node); | |||
FOREACH_MATCH(spec_results) { | |||
const SordNode* spec = sord_iter_get_node(spec_results, SORD_SUBJECT); | |||
lilv_world_add_spec(world, spec, bundle_node); | |||
} | |||
sord_iter_free(spec_results); | |||
serd_node_free(&manifest_uri); | |||
} | |||
static void | |||
load_dir_entry(const char* dir, const char* name, void* data) | |||
{ | |||
LilvWorld* world = (LilvWorld*)data; | |||
if (!strcmp(name, ".") || !strcmp(name, "..")) | |||
return; | |||
const char* scheme = (dir[0] == '/') ? "file://" : "file:///"; | |||
char* uri = lilv_strjoin(scheme, dir, "/", name, "/", NULL); | |||
LilvNode* uri_val = lilv_new_uri(world, uri); | |||
lilv_world_load_bundle(world, uri_val); | |||
lilv_node_free(uri_val); | |||
free(uri); | |||
} | |||
/** Load all bundles in the directory at @a dir_path. */ | |||
static void | |||
lilv_world_load_directory(LilvWorld* world, const char* dir_path) | |||
{ | |||
char* path = lilv_expand(dir_path); | |||
if (!path) { | |||
LILV_WARNF("Empty path `%s'\n", path); | |||
return; | |||
} | |||
lilv_dir_for_each(path, world, load_dir_entry); | |||
free(path); | |||
} | |||
static bool | |||
is_path_sep(char c) | |||
{ | |||
return c == LILV_PATH_SEP[0]; | |||
} | |||
static const char* | |||
first_path_sep(const char* path) | |||
{ | |||
for (const char* p = path; *p != '\0'; ++p) { | |||
if (is_path_sep(*p)) { | |||
return p; | |||
} | |||
} | |||
return NULL; | |||
} | |||
/** Load all bundles found in @a lv2_path. | |||
* @param lv2_path A colon-delimited list of directories. These directories | |||
* should contain LV2 bundle directories (ie the search path is a list of | |||
* parent directories of bundles, not a list of bundle directories). | |||
*/ | |||
static void | |||
lilv_world_load_path(LilvWorld* world, | |||
const char* lv2_path) | |||
{ | |||
while (lv2_path[0] != '\0') { | |||
const char* const sep = first_path_sep(lv2_path); | |||
if (sep) { | |||
const size_t dir_len = sep - lv2_path; | |||
char* const dir = (char*)malloc(dir_len + 1); | |||
memcpy(dir, lv2_path, dir_len); | |||
dir[dir_len] = '\0'; | |||
lilv_world_load_directory(world, dir); | |||
free(dir); | |||
lv2_path += dir_len + 1; | |||
} else { | |||
lilv_world_load_directory(world, lv2_path); | |||
lv2_path = "\0"; | |||
} | |||
} | |||
} | |||
static void | |||
lilv_world_load_specifications(LilvWorld* world) | |||
{ | |||
for (LilvSpec* spec = world->specs; spec; spec = spec->next) { | |||
LILV_FOREACH(nodes, f, spec->data_uris) { | |||
LilvNode* file = (LilvNode*)lilv_collection_get(spec->data_uris, f); | |||
const SerdNode* node = sord_node_to_serd_node(file->node); | |||
SerdEnv* env = serd_env_new(node); | |||
SerdReader* reader = sord_new_reader(world->model, env, | |||
SERD_TURTLE, NULL); | |||
serd_reader_add_blank_prefix(reader, | |||
lilv_world_blank_node_prefix(world)); | |||
serd_reader_read_file(reader, node->buf); | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
} | |||
} | |||
} | |||
static void | |||
lilv_world_load_plugin_classes(LilvWorld* world) | |||
{ | |||
/* FIXME: This loads all classes, not just lv2:Plugin subclasses. | |||
However, if the host gets all the classes via lilv_plugin_class_get_children | |||
starting with lv2:Plugin as the root (which is e.g. how a host would build | |||
a menu), they won't be seen anyway... | |||
*/ | |||
SordIter* classes = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.rdf_a, | |||
world->uris.rdfs_Class, | |||
NULL); | |||
FOREACH_MATCH(classes) { | |||
const SordNode* class_node = sord_iter_get_node(classes, SORD_SUBJECT); | |||
// Get parents (superclasses) | |||
SordIter* parents = sord_search( | |||
world->model, | |||
class_node, | |||
world->uris.rdfs_subClassOf, | |||
NULL, | |||
NULL); | |||
if (sord_iter_end(parents)) { | |||
sord_iter_free(parents); | |||
continue; | |||
} | |||
const SordNode* parent_node = sord_iter_get_node(parents, SORD_OBJECT); | |||
sord_iter_free(parents); | |||
if (!sord_node_get_type(parent_node) == SORD_URI) { | |||
// Class parent is not a resource, ignore (e.g. owl restriction) | |||
continue; | |||
} | |||
// Get labels | |||
SordIter* labels = sord_search( | |||
world->model, | |||
class_node, | |||
world->uris.rdfs_label, | |||
NULL, | |||
NULL); | |||
if (sord_iter_end(labels)) { | |||
sord_iter_free(labels); | |||
continue; | |||
} | |||
const SordNode* label_node = sord_iter_get_node(labels, SORD_OBJECT); | |||
const uint8_t* label = sord_node_get_string(label_node); | |||
sord_iter_free(labels); | |||
LilvPluginClass* pclass = lilv_plugin_class_new( | |||
world, parent_node, class_node, (const char*)label); | |||
if (pclass) { | |||
zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL); | |||
} | |||
} | |||
sord_iter_free(classes); | |||
} | |||
LILV_API | |||
void | |||
lilv_world_load_all(LilvWorld* world) | |||
{ | |||
const char* lv2_path = getenv("LV2_PATH"); | |||
if (!lv2_path) | |||
lv2_path = LILV_DEFAULT_LV2_PATH; | |||
// Discover bundles and read all manifest files into model | |||
lilv_world_load_path(world, lv2_path); | |||
LILV_FOREACH(plugins, p, world->plugins) { | |||
const LilvPlugin* plugin = (const LilvPlugin*)lilv_collection_get( | |||
(ZixTree*)world->plugins, p); | |||
// ?new dc:replaces plugin | |||
SordIter* replacement = sord_search( | |||
world->model, | |||
NULL, | |||
world->uris.dc_replaces, | |||
lilv_node_as_node(lilv_plugin_get_uri(plugin)), | |||
NULL); | |||
if (!sord_iter_end(replacement)) { | |||
/* TODO: Check if replacement is actually a known plugin, | |||
though this is expensive... | |||
*/ | |||
((LilvPlugin*)plugin)->replaced = true; | |||
} | |||
sord_iter_free(replacement); | |||
} | |||
// Query out things to cache | |||
lilv_world_load_specifications(world); | |||
lilv_world_load_plugin_classes(world); | |||
} | |||
LILV_API | |||
int | |||
lilv_world_load_resource(LilvWorld* world, | |||
const LilvNode* resource) | |||
{ | |||
if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) { | |||
LILV_ERRORF("Node `%s' is not a resource\n", | |||
sord_node_get_string(resource->node)); | |||
return -1; | |||
} | |||
int n_read = 0; | |||
SordIter* files = sord_search(world->model, | |||
resource->node, | |||
world->uris.rdfs_seeAlso, | |||
NULL, NULL); | |||
FOREACH_MATCH(files) { | |||
const SordNode* file = sord_iter_get_node(files, SORD_OBJECT); | |||
const uint8_t* str = sord_node_get_string(file); | |||
LilvNode* file_node = lilv_node_new_from_node(world, file); | |||
ZixTreeIter* iter; | |||
if (zix_tree_find((ZixTree*)world->loaded_files, file_node, &iter)) { | |||
if (sord_node_get_type(file) == SORD_URI) { | |||
const SerdNode* base = sord_node_to_serd_node(file); | |||
SerdEnv* env = serd_env_new(base); | |||
SerdReader* reader = sord_new_reader( | |||
world->model, env, SERD_TURTLE, (SordNode*)file); | |||
serd_reader_add_blank_prefix( | |||
reader, lilv_world_blank_node_prefix(world)); | |||
if (!serd_reader_read_file(reader, str)) { | |||
++n_read; | |||
zix_tree_insert( | |||
(ZixTree*)world->loaded_files, file_node, NULL); | |||
file_node = NULL; // prevent deletion... | |||
} else { | |||
LILV_ERRORF("Error loading resource `%s'\n", str); | |||
} | |||
serd_reader_free(reader); | |||
serd_env_free(env); | |||
} else { | |||
LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", str); | |||
} | |||
} | |||
lilv_node_free(file_node); // ...here | |||
} | |||
sord_iter_free(files); | |||
return n_read; | |||
} | |||
LILV_API | |||
const LilvPluginClass* | |||
lilv_world_get_plugin_class(const LilvWorld* world) | |||
{ | |||
return world->lv2_plugin_class; | |||
} | |||
LILV_API | |||
const LilvPluginClasses* | |||
lilv_world_get_plugin_classes(const LilvWorld* world) | |||
{ | |||
return world->plugin_classes; | |||
} | |||
LILV_API | |||
const LilvPlugins* | |||
lilv_world_get_all_plugins(const LilvWorld* world) | |||
{ | |||
return world->plugins; | |||
} |
@@ -0,0 +1,88 @@ | |||
/* | |||
Copyright 2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef ZIX_COMMON_H | |||
#define ZIX_COMMON_H | |||
/** | |||
@addtogroup zix | |||
@{ | |||
*/ | |||
/** @cond */ | |||
#ifdef ZIX_SHARED | |||
# ifdef _WIN32 | |||
# define ZIX_LIB_IMPORT __declspec(dllimport) | |||
# define ZIX_LIB_EXPORT __declspec(dllexport) | |||
# else | |||
# define ZIX_LIB_IMPORT __attribute__((visibility("default"))) | |||
# define ZIX_LIB_EXPORT __attribute__((visibility("default"))) | |||
# endif | |||
# ifdef ZIX_INTERNAL | |||
# define ZIX_API ZIX_LIB_EXPORT | |||
# else | |||
# define ZIX_API ZIX_LIB_IMPORT | |||
# endif | |||
# define ZIX_PRIVATE static | |||
#elif defined(ZIX_INLINE) | |||
# define ZIX_API static inline | |||
# define ZIX_PRIVATE static inline | |||
#else | |||
# define ZIX_API | |||
# define ZIX_PRIVATE static | |||
#endif | |||
/** @endcond */ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
typedef enum { | |||
ZIX_STATUS_SUCCESS, | |||
ZIX_STATUS_ERROR, | |||
ZIX_STATUS_NO_MEM, | |||
ZIX_STATUS_NOT_FOUND, | |||
ZIX_STATUS_EXISTS, | |||
ZIX_STATUS_BAD_ARG, | |||
ZIX_STATUS_BAD_PERMS, | |||
} ZixStatus; | |||
/** | |||
Function for comparing two elements. | |||
*/ | |||
typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); | |||
/** | |||
Function for testing equality of two elements. | |||
*/ | |||
typedef bool (*ZixEqualFunc)(const void* a, const void* b); | |||
/** | |||
Function to destroy an element. | |||
*/ | |||
typedef void (*ZixDestroyFunc)(void* ptr); | |||
/** | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* ZIX_COMMON_H */ |
@@ -0,0 +1,716 @@ | |||
/* | |||
Copyright 2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <stdint.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "zix/common.h" | |||
#include "zix/tree.h" | |||
typedef struct ZixTreeNodeImpl ZixTreeNode; | |||
struct ZixTreeImpl { | |||
ZixTreeNode* root; | |||
ZixDestroyFunc destroy; | |||
ZixComparator cmp; | |||
void* cmp_data; | |||
size_t size; | |||
bool allow_duplicates; | |||
}; | |||
struct ZixTreeNodeImpl { | |||
void* data; | |||
struct ZixTreeNodeImpl* left; | |||
struct ZixTreeNodeImpl* right; | |||
struct ZixTreeNodeImpl* parent; | |||
int_fast8_t balance; | |||
}; | |||
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |||
// Uncomment these for debugging features | |||
// #define ZIX_TREE_DUMP 1 | |||
// #define ZIX_TREE_VERIFY 1 | |||
// #define ZIX_TREE_HYPER_VERIFY 1 | |||
#if defined(ZIX_TREE_VERIFY) || defined(ZIX_TREE_HYPER_VERIFY) | |||
# include "tree_debug.h" | |||
# define ASSERT_BALANCE(n) assert(verify_balance(n)) | |||
#else | |||
# define ASSERT_BALANCE(n) | |||
#endif | |||
#ifdef ZIX_TREE_DUMP | |||
# include "tree_debug.h" | |||
# define DUMP(t) zix_tree_print(t->root, 0) | |||
# define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) | |||
#else | |||
# define DUMP(t) | |||
# define DEBUG_PRINTF(fmt, ...) | |||
#endif | |||
ZIX_API ZixTree* | |||
zix_tree_new(bool allow_duplicates, | |||
ZixComparator cmp, | |||
void* cmp_data, | |||
ZixDestroyFunc destroy) | |||
{ | |||
ZixTree* t = (ZixTree*)malloc(sizeof(ZixTree)); | |||
t->root = NULL; | |||
t->destroy = destroy; | |||
t->cmp = cmp; | |||
t->cmp_data = cmp_data; | |||
t->size = 0; | |||
t->allow_duplicates = allow_duplicates; | |||
return t; | |||
} | |||
ZIX_PRIVATE void | |||
zix_tree_free_rec(ZixTree* t, ZixTreeNode* n) | |||
{ | |||
if (n) { | |||
zix_tree_free_rec(t, n->left); | |||
zix_tree_free_rec(t, n->right); | |||
if (t->destroy) { | |||
t->destroy(n->data); | |||
} | |||
free(n); | |||
} | |||
} | |||
ZIX_API void | |||
zix_tree_free(ZixTree* t) | |||
{ | |||
if (t) { | |||
zix_tree_free_rec(t, t->root); | |||
free(t); | |||
} | |||
} | |||
ZIX_API size_t | |||
zix_tree_size(const ZixTree* t) | |||
{ | |||
return t->size; | |||
} | |||
ZIX_PRIVATE void | |||
rotate(ZixTreeNode* p, ZixTreeNode* q) | |||
{ | |||
assert(q->parent == p); | |||
assert(p->left == q || p->right == q); | |||
q->parent = p->parent; | |||
if (q->parent) { | |||
if (q->parent->left == p) { | |||
q->parent->left = q; | |||
} else { | |||
q->parent->right = q; | |||
} | |||
} | |||
if (p->right == q) { | |||
// Rotate left | |||
p->right = q->left; | |||
q->left = p; | |||
if (p->right) { | |||
p->right->parent = p; | |||
} | |||
} else { | |||
// Rotate right | |||
assert(p->left == q); | |||
p->left = q->right; | |||
q->right = p; | |||
if (p->left) { | |||
p->left->parent = p; | |||
} | |||
} | |||
p->parent = q; | |||
} | |||
/** | |||
* Rotate left about @a p. | |||
* | |||
* p q | |||
* / \ / \ | |||
* A q => p C | |||
* / \ / \ | |||
* B C A B | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_left(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->right; | |||
*height_change = (q->balance == 0) ? 0 : -1; | |||
DEBUG_PRINTF("LL %ld\n", (intptr_t)p->data); | |||
assert(p->balance == 2); | |||
assert(q->balance == 0 || q->balance == 1); | |||
rotate(p, q); | |||
// p->balance -= 1 + MAX(0, q->balance); | |||
// q->balance -= 1 - MIN(0, p->balance); | |||
--q->balance; | |||
p->balance = -(q->balance); | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
return q; | |||
} | |||
/** | |||
* Rotate right about @a p. | |||
* | |||
* p q | |||
* / \ / \ | |||
* q C => A p | |||
* / \ / \ | |||
* A B B C | |||
* | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_right(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->left; | |||
*height_change = (q->balance == 0) ? 0 : -1; | |||
DEBUG_PRINTF("RR %ld\n", (intptr_t)p->data); | |||
assert(p->balance == -2); | |||
assert(q->balance == 0 || q->balance == -1); | |||
rotate(p, q); | |||
// p->balance += 1 - MIN(0, q->balance); | |||
// q->balance += 1 + MAX(0, p->balance); | |||
++q->balance; | |||
p->balance = -(q->balance); | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
return q; | |||
} | |||
/** | |||
* Rotate left about @a p->left then right about @a p. | |||
* | |||
* p r | |||
* / \ / \ | |||
* q D => q p | |||
* / \ / \ / \ | |||
* A r A B C D | |||
* / \ | |||
* B C | |||
* | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_left_right(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->left; | |||
ZixTreeNode* const r = q->right; | |||
assert(p->balance == -2); | |||
assert(q->balance == 1); | |||
assert(r->balance == -1 || r->balance == 0 || r->balance == 1); | |||
DEBUG_PRINTF("LR %ld P: %2d Q: %2d R: %2d\n", | |||
(intptr_t)p->data, p->balance, q->balance, r->balance); | |||
rotate(q, r); | |||
rotate(p, r); | |||
q->balance -= 1 + MAX(0, r->balance); | |||
p->balance += 1 - MIN(MIN(0, r->balance) - 1, r->balance + q->balance); | |||
// r->balance += MAX(0, p->balance) + MIN(0, q->balance); | |||
// p->balance = (p->left && p->right) ? -MIN(r->balance, 0) : 0; | |||
// q->balance = - MAX(r->balance, 0); | |||
r->balance = 0; | |||
*height_change = -1; | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
ASSERT_BALANCE(r); | |||
return r; | |||
} | |||
/** | |||
* Rotate right about @a p->right then right about @a p. | |||
* | |||
* p r | |||
* / \ / \ | |||
* A q => p q | |||
* / \ / \ / \ | |||
* r D A B C D | |||
* / \ | |||
* B C | |||
* | |||
*/ | |||
ZIX_PRIVATE ZixTreeNode* | |||
rotate_right_left(ZixTreeNode* p, int* height_change) | |||
{ | |||
ZixTreeNode* const q = p->right; | |||
ZixTreeNode* const r = q->left; | |||
assert(p->balance == 2); | |||
assert(q->balance == -1); | |||
assert(r->balance == -1 || r->balance == 0 || r->balance == 1); | |||
DEBUG_PRINTF("RL %ld P: %2d Q: %2d R: %2d\n", | |||
(intptr_t)p->data, p->balance, q->balance, r->balance); | |||
rotate(q, r); | |||
rotate(p, r); | |||
q->balance += 1 - MIN(0, r->balance); | |||
p->balance -= 1 + MAX(MAX(0, r->balance) + 1, r->balance + q->balance); | |||
// r->balance += MAX(0, q->balance) + MIN(0, p->balance); | |||
// p->balance = (p->left && p->right) ? -MAX(r->balance, 0) : 0; | |||
// q->balance = - MIN(r->balance, 0); | |||
r->balance = 0; | |||
// assert(r->balance == 0); | |||
*height_change = -1; | |||
ASSERT_BALANCE(p); | |||
ASSERT_BALANCE(q); | |||
ASSERT_BALANCE(r); | |||
return r; | |||
} | |||
ZIX_PRIVATE ZixTreeNode* | |||
zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) | |||
{ | |||
#ifdef ZIX_TREE_HYPER_VERIFY | |||
const size_t old_height = height(node); | |||
#endif | |||
DEBUG_PRINTF("REBALANCE %ld (%d)\n", (intptr_t)node->data, node->balance); | |||
*height_change = 0; | |||
const bool is_root = !node->parent; | |||
assert((is_root && t->root == node) || (!is_root && t->root != node)); | |||
ZixTreeNode* replacement = node; | |||
if (node->balance == -2) { | |||
assert(node->left); | |||
if (node->left->balance == 1) { | |||
replacement = rotate_left_right(node, height_change); | |||
} else { | |||
replacement = rotate_right(node, height_change); | |||
} | |||
} else if (node->balance == 2) { | |||
assert(node->right); | |||
if (node->right->balance == -1) { | |||
replacement = rotate_right_left(node, height_change); | |||
} else { | |||
replacement = rotate_left(node, height_change); | |||
} | |||
} | |||
if (is_root) { | |||
assert(!replacement->parent); | |||
t->root = replacement; | |||
} | |||
DUMP(t); | |||
#ifdef ZIX_TREE_HYPER_VERIFY | |||
assert(old_height + *height_change == height(replacement)); | |||
#endif | |||
return replacement; | |||
} | |||
ZIX_API ZixStatus | |||
zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti) | |||
{ | |||
DEBUG_PRINTF("**** INSERT %ld\n", (intptr_t)e); | |||
int cmp = 0; | |||
ZixTreeNode* n = t->root; | |||
ZixTreeNode* p = NULL; | |||
// Find the parent p of e | |||
while (n) { | |||
p = n; | |||
cmp = t->cmp(e, n->data, t->cmp_data); | |||
if (cmp < 0) { | |||
n = n->left; | |||
} else if (cmp > 0) { | |||
n = n->right; | |||
} else if (t->allow_duplicates) { | |||
n = n->right; | |||
} else { | |||
if (ti) { | |||
*ti = n; | |||
} | |||
DEBUG_PRINTF("%ld EXISTS!\n", (intptr_t)e); | |||
return ZIX_STATUS_EXISTS; | |||
} | |||
} | |||
// Allocate a new node n | |||
if (!(n = (ZixTreeNode*)malloc(sizeof(ZixTreeNode)))) { | |||
return ZIX_STATUS_NO_MEM; | |||
} | |||
memset(n, '\0', sizeof(ZixTreeNode)); | |||
n->data = e; | |||
n->balance = 0; | |||
if (ti) { | |||
*ti = n; | |||
} | |||
bool p_height_increased = false; | |||
// Make p the parent of n | |||
n->parent = p; | |||
if (!p) { | |||
t->root = n; | |||
} else { | |||
if (cmp < 0) { | |||
assert(!p->left); | |||
assert(p->balance == 0 || p->balance == 1); | |||
p->left = n; | |||
--p->balance; | |||
p_height_increased = !p->right; | |||
} else { | |||
assert(!p->right); | |||
assert(p->balance == 0 || p->balance == -1); | |||
p->right = n; | |||
++p->balance; | |||
p_height_increased = !p->left; | |||
} | |||
} | |||
DUMP(t); | |||
// Rebalance if necessary (at most 1 rotation) | |||
assert(!p || p->balance == -1 || p->balance == 0 || p->balance == 1); | |||
if (p && p_height_increased) { | |||
int height_change = 0; | |||
for (ZixTreeNode* i = p; i && i->parent; i = i->parent) { | |||
if (i == i->parent->left) { | |||
if (--i->parent->balance == -2) { | |||
zix_tree_rebalance(t, i->parent, &height_change); | |||
break; | |||
} | |||
} else { | |||
assert(i == i->parent->right); | |||
if (++i->parent->balance == 2) { | |||
zix_tree_rebalance(t, i->parent, &height_change); | |||
break; | |||
} | |||
} | |||
if (i->parent->balance == 0) { | |||
break; | |||
} | |||
} | |||
} | |||
DUMP(t); | |||
++t->size; | |||
#ifdef ZIX_TREE_VERIFY | |||
if (!verify(t, t->root)) { | |||
return ZIX_STATUS_ERROR; | |||
} | |||
#endif | |||
return ZIX_STATUS_SUCCESS; | |||
} | |||
ZIX_API ZixStatus | |||
zix_tree_remove(ZixTree* t, ZixTreeIter* ti) | |||
{ | |||
ZixTreeNode* const n = ti; | |||
ZixTreeNode** pp = NULL; // parent pointer | |||
ZixTreeNode* to_balance = n->parent; // lowest node to balance | |||
int8_t d_balance = 0; // delta(balance) for n->parent | |||
DEBUG_PRINTF("*** REMOVE %ld\n", (intptr_t)n->data); | |||
if ((n == t->root) && !n->left && !n->right) { | |||
t->root = NULL; | |||
if (t->destroy) { | |||
t->destroy(n->data); | |||
} | |||
free(n); | |||
--t->size; | |||
assert(t->size == 0); | |||
return ZIX_STATUS_SUCCESS; | |||
} | |||
// Set pp to the parent pointer to n, if applicable | |||
if (n->parent) { | |||
assert(n->parent->left == n || n->parent->right == n); | |||
if (n->parent->left == n) { // n is left child | |||
pp = &n->parent->left; | |||
d_balance = 1; | |||
} else { // n is right child | |||
assert(n->parent->right == n); | |||
pp = &n->parent->right; | |||
d_balance = -1; | |||
} | |||
} | |||
assert(!pp || *pp == n); | |||
int height_change = 0; | |||
if (!n->left && !n->right) { | |||
// n is a leaf, just remove it | |||
if (pp) { | |||
*pp = NULL; | |||
to_balance = n->parent; | |||
height_change = (!n->parent->left && !n->parent->right) ? -1 : 0; | |||
} | |||
} else if (!n->left) { | |||
// Replace n with right (only) child | |||
if (pp) { | |||
*pp = n->right; | |||
to_balance = n->parent; | |||
} else { | |||
t->root = n->right; | |||
} | |||
n->right->parent = n->parent; | |||
height_change = -1; | |||
} else if (!n->right) { | |||
// Replace n with left (only) child | |||
if (pp) { | |||
*pp = n->left; | |||
to_balance = n->parent; | |||
} else { | |||
t->root = n->left; | |||
} | |||
n->left->parent = n->parent; | |||
height_change = -1; | |||
} else { | |||
// Replace n with in-order successor (leftmost child of right subtree) | |||
ZixTreeNode* replace = n->right; | |||
while (replace->left) { | |||
assert(replace->left->parent == replace); | |||
replace = replace->left; | |||
} | |||
// Remove replace from parent (replace_p) | |||
if (replace->parent->left == replace) { | |||
height_change = replace->parent->right ? 0 : -1; | |||
d_balance = 1; | |||
to_balance = replace->parent; | |||
replace->parent->left = replace->right; | |||
} else { | |||
assert(replace->parent == n); | |||
height_change = replace->parent->left ? 0 : -1; | |||
d_balance = -1; | |||
to_balance = replace->parent; | |||
replace->parent->right = replace->right; | |||
} | |||
if (to_balance == n) { | |||
to_balance = replace; | |||
} | |||
if (replace->right) { | |||
replace->right->parent = replace->parent; | |||
} | |||
replace->balance = n->balance; | |||
// Swap node to delete with replace | |||
if (pp) { | |||
*pp = replace; | |||
} else { | |||
assert(t->root == n); | |||
t->root = replace; | |||
} | |||
replace->parent = n->parent; | |||
replace->left = n->left; | |||
n->left->parent = replace; | |||
replace->right = n->right; | |||
if (n->right) { | |||
n->right->parent = replace; | |||
} | |||
assert(!replace->parent | |||
|| replace->parent->left == replace | |||
|| replace->parent->right == replace); | |||
} | |||
// Rebalance starting at to_balance upwards. | |||
for (ZixTreeNode* i = to_balance; i; i = i->parent) { | |||
i->balance += d_balance; | |||
if (d_balance == 0 || i->balance == -1 || i->balance == 1) { | |||
break; | |||
} | |||
assert(i != n); | |||
i = zix_tree_rebalance(t, i, &height_change); | |||
if (i->balance == 0) { | |||
height_change = -1; | |||
} | |||
if (i->parent) { | |||
if (i == i->parent->left) { | |||
d_balance = height_change * -1; | |||
} else { | |||
assert(i == i->parent->right); | |||
d_balance = height_change; | |||
} | |||
} | |||
} | |||
DUMP(t); | |||
if (t->destroy) { | |||
t->destroy(n->data); | |||
} | |||
free(n); | |||
--t->size; | |||
#ifdef ZIX_TREE_VERIFY | |||
if (!verify(t, t->root)) { | |||
return ZIX_STATUS_ERROR; | |||
} | |||
#endif | |||
return ZIX_STATUS_SUCCESS; | |||
} | |||
ZIX_API ZixStatus | |||
zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti) | |||
{ | |||
ZixTreeNode* n = t->root; | |||
while (n) { | |||
const int cmp = t->cmp(e, n->data, t->cmp_data); | |||
if (cmp == 0) { | |||
break; | |||
} else if (cmp < 0) { | |||
n = n->left; | |||
} else { | |||
n = n->right; | |||
} | |||
} | |||
*ti = n; | |||
return (n) ? ZIX_STATUS_SUCCESS : ZIX_STATUS_NOT_FOUND; | |||
} | |||
ZIX_API void* | |||
zix_tree_get(ZixTreeIter* ti) | |||
{ | |||
return ti ? ti->data : NULL; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_begin(ZixTree* t) | |||
{ | |||
if (!t->root) { | |||
return NULL; | |||
} | |||
ZixTreeNode* n = t->root; | |||
while (n->left) { | |||
n = n->left; | |||
} | |||
return n; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_end(ZixTree* t) | |||
{ | |||
return NULL; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rbegin(ZixTree* t) | |||
{ | |||
if (!t->root) { | |||
return NULL; | |||
} | |||
ZixTreeNode* n = t->root; | |||
while (n->right) { | |||
n = n->right; | |||
} | |||
return n; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rend(ZixTree* t) | |||
{ | |||
return NULL; | |||
} | |||
ZIX_API bool | |||
zix_tree_iter_is_end(ZixTreeIter* i) | |||
{ | |||
return !i; | |||
} | |||
ZIX_API bool | |||
zix_tree_iter_is_rend(ZixTreeIter* i) | |||
{ | |||
return !i; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_next(ZixTreeIter* i) | |||
{ | |||
if (!i) { | |||
return NULL; | |||
} | |||
if (i->right) { | |||
i = i->right; | |||
while (i->left) { | |||
i = i->left; | |||
} | |||
} else { | |||
while (i->parent && i->parent->right == i) { // i is a right child | |||
i = i->parent; | |||
} | |||
i = i->parent; | |||
} | |||
return i; | |||
} | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_prev(ZixTreeIter* i) | |||
{ | |||
if (!i) { | |||
return NULL; | |||
} | |||
if (i->left) { | |||
i = i->left; | |||
while (i->right) { | |||
i = i->right; | |||
} | |||
} else { | |||
while (i->parent && i->parent->left == i) { // i is a left child | |||
i = i->parent; | |||
} | |||
i = i->parent; | |||
} | |||
return i; | |||
} |
@@ -0,0 +1,148 @@ | |||
/* | |||
Copyright 2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#ifndef ZIX_TREE_H | |||
#define ZIX_TREE_H | |||
#include <stddef.h> | |||
#include "zix/common.h" | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
/** | |||
@addtogroup zix | |||
@{ | |||
@name Tree | |||
@{ | |||
*/ | |||
/** | |||
A balanced binary search tree. | |||
*/ | |||
typedef struct ZixTreeImpl ZixTree; | |||
/** | |||
An iterator over a @ref ZixTree. | |||
*/ | |||
typedef struct ZixTreeNodeImpl ZixTreeIter; | |||
/** | |||
Create a new (empty) tree. | |||
*/ | |||
ZIX_API ZixTree* | |||
zix_tree_new(bool allow_duplicates, | |||
ZixComparator cmp, | |||
void* cmp_data, | |||
ZixDestroyFunc destroy); | |||
/** | |||
Free @a t. | |||
*/ | |||
ZIX_API void | |||
zix_tree_free(ZixTree* t); | |||
/** | |||
Return the number of elements in @a t. | |||
*/ | |||
ZIX_API size_t | |||
zix_tree_size(const ZixTree* t); | |||
/** | |||
Insert the element @a e into @a t and point @a ti at the new element. | |||
*/ | |||
ZIX_API ZixStatus | |||
zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti); | |||
/** | |||
Remove the item pointed at by @a ti from @a t. | |||
*/ | |||
ZIX_API ZixStatus | |||
zix_tree_remove(ZixTree* t, ZixTreeIter* ti); | |||
/** | |||
Set @a ti to an element equal to @a e in @a t. | |||
If no such item exists, @a ti is set to NULL. | |||
*/ | |||
ZIX_API ZixStatus | |||
zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti); | |||
/** | |||
Return the data associated with the given tree item. | |||
*/ | |||
ZIX_API void* | |||
zix_tree_get(ZixTreeIter* ti); | |||
/** | |||
Return an iterator to the first (smallest) element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_begin(ZixTree* t); | |||
/** | |||
Return an iterator the the element one past the last element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_end(ZixTree* t); | |||
/** | |||
Return true iff @a i is an iterator to the end of its tree. | |||
*/ | |||
ZIX_API bool | |||
zix_tree_iter_is_end(ZixTreeIter* i); | |||
/** | |||
Return an iterator to the last (largest) element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rbegin(ZixTree* t); | |||
/** | |||
Return an iterator the the element one before the first element in @a t. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_rend(ZixTree* t); | |||
/** | |||
Return true iff @a i is an iterator to the reverse end of its tree. | |||
*/ | |||
ZIX_API bool | |||
zix_tree_iter_is_rend(ZixTreeIter* i); | |||
/** | |||
Return an iterator that points to the element one past @a i. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_next(ZixTreeIter* i); | |||
/** | |||
Return an iterator that points to the element one before @a i. | |||
*/ | |||
ZIX_API ZixTreeIter* | |||
zix_tree_iter_prev(ZixTreeIter* i); | |||
/** | |||
@} | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* ZIX_TREE_H */ |
@@ -0,0 +1,7 @@ | |||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> . | |||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
<http://example.org/lilv-test-plugin> | |||
a lv2:Plugin ; | |||
lv2:binary <test_plugin@SHLIB_EXT@> ; | |||
rdfs:seeAlso <test_plugin.ttl> . |
@@ -0,0 +1,384 @@ | |||
/* | |||
Lilv Test Plugin | |||
Copyright 2011 David Robillard <d@drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/lv2plug.in/ns/ext/atom/atom.h" | |||
#include "lv2/lv2plug.in/ns/ext/state/state.h" | |||
#include "lv2/lv2plug.in/ns/ext/urid/urid.h" | |||
#include "lv2/lv2plug.in/ns/lv2core/lv2.h" | |||
#define TEST_URI "http://example.org/lilv-test-plugin" | |||
enum { | |||
TEST_INPUT = 0, | |||
TEST_OUTPUT = 1 | |||
}; | |||
typedef struct { | |||
LV2_URID_Map* map; | |||
struct { | |||
LV2_URID atom_Float; | |||
} uris; | |||
char* tmp_file_path; | |||
char* rec_file_path; | |||
FILE* rec_file; | |||
float* input; | |||
float* output; | |||
unsigned num_runs; | |||
} Test; | |||
static void | |||
cleanup(LV2_Handle instance) | |||
{ | |||
Test* test = (Test*)instance; | |||
if (test->rec_file) { | |||
fclose(test->rec_file); | |||
} | |||
free(test->tmp_file_path); | |||
free(test->rec_file_path); | |||
free(instance); | |||
} | |||
static void | |||
connect_port(LV2_Handle instance, | |||
uint32_t port, | |||
void* data) | |||
{ | |||
Test* test = (Test*)instance; | |||
switch (port) { | |||
case TEST_INPUT: | |||
test->input = (float*)data; | |||
break; | |||
case TEST_OUTPUT: | |||
test->output = (float*)data; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
static LV2_Handle | |||
instantiate(const LV2_Descriptor* descriptor, | |||
double rate, | |||
const char* path, | |||
const LV2_Feature* const* features) | |||
{ | |||
Test* test = (Test*)malloc(sizeof(Test)); | |||
if (!test) { | |||
return NULL; | |||
} | |||
test->map = NULL; | |||
test->input = NULL; | |||
test->output = NULL; | |||
test->num_runs = 0; | |||
test->tmp_file_path = (char*)malloc(L_tmpnam); | |||
test->rec_file_path = NULL; | |||
test->rec_file = NULL; | |||
tmpnam(test->tmp_file_path); | |||
LV2_State_Make_Path* make_path = NULL; | |||
for (int i = 0; features[i]; ++i) { | |||
if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { | |||
test->map = (LV2_URID_Map*)features[i]->data; | |||
test->uris.atom_Float = test->map->map( | |||
test->map->handle, LV2_ATOM__Float); | |||
} else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { | |||
make_path = (LV2_State_Make_Path*)features[i]->data; | |||
} | |||
} | |||
if (!test->map) { | |||
fprintf(stderr, "Host does not support urid:map\n"); | |||
free(test); | |||
return NULL; | |||
} | |||
if (make_path) { | |||
test->rec_file_path = make_path->path(make_path->handle, "recfile"); | |||
if (!(test->rec_file = fopen(test->rec_file_path, "w"))) { | |||
fprintf(stderr, "ERROR: Failed to open rec file\n"); | |||
} | |||
fprintf(test->rec_file, "instantiate\n"); | |||
} | |||
return (LV2_Handle)test; | |||
} | |||
static void | |||
run(LV2_Handle instance, | |||
uint32_t sample_count) | |||
{ | |||
Test* test = (Test*)instance; | |||
*test->output = *test->input; | |||
if (sample_count == 1) { | |||
++test->num_runs; | |||
} else if (sample_count == 2 && test->rec_file) { | |||
// Append to rec file (changes size) | |||
fprintf(test->rec_file, "run\n"); | |||
} else if (sample_count == 3 && test->rec_file) { | |||
// Change the first byte of rec file (doesn't change size) | |||
fseek(test->rec_file, 0, SEEK_SET); | |||
fprintf(test->rec_file, "X"); | |||
fseek(test->rec_file, 0, SEEK_END); | |||
} | |||
} | |||
static uint32_t | |||
map_uri(Test* plugin, const char* uri) | |||
{ | |||
return plugin->map->map(plugin->map->handle, uri); | |||
} | |||
static LV2_State_Status | |||
save(LV2_Handle instance, | |||
LV2_State_Store_Function store, | |||
void* callback_data, | |||
uint32_t flags, | |||
const LV2_Feature* const* features) | |||
{ | |||
Test* plugin = (Test*)instance; | |||
LV2_State_Map_Path* map_path = NULL; | |||
LV2_State_Make_Path* make_path = NULL; | |||
for (int i = 0; features && features[i]; ++i) { | |||
if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { | |||
map_path = (LV2_State_Map_Path*)features[i]->data; | |||
} else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { | |||
make_path = (LV2_State_Make_Path*)features[i]->data; | |||
} | |||
} | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/greeting"), | |||
"hello", | |||
strlen("hello") + 1, | |||
map_uri(plugin, LV2_ATOM__String), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint32_t urid = map_uri(plugin, "http://example.org/urivalue"); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/uri"), | |||
&urid, | |||
sizeof(uint32_t), | |||
map_uri(plugin, LV2_ATOM__URID), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/num-runs"), | |||
&plugin->num_runs, | |||
sizeof(plugin->num_runs), | |||
map_uri(plugin, LV2_ATOM__Int), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const float two = 2.0f; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/two"), | |||
&two, | |||
sizeof(two), | |||
map_uri(plugin, LV2_ATOM__Float), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint32_t affirmative = 1; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/true"), | |||
&affirmative, | |||
sizeof(affirmative), | |||
map_uri(plugin, LV2_ATOM__Bool), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint32_t negative = 0; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/false"), | |||
&negative, | |||
sizeof(negative), | |||
map_uri(plugin, LV2_ATOM__Bool), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
const uint8_t blob[] = "I am a blob of arbitrary data."; | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/blob"), | |||
blob, | |||
sizeof(blob), | |||
map_uri(plugin, "http://example.org/SomeUnknownType"), | |||
LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
if (map_path) { | |||
FILE* file = fopen(plugin->tmp_file_path, "w"); | |||
fprintf(file, "Hello\n"); | |||
fclose(file); | |||
char* apath = map_path->abstract_path(map_path->handle, | |||
plugin->tmp_file_path); | |||
char* apath2 = map_path->abstract_path(map_path->handle, | |||
plugin->tmp_file_path); | |||
if (strcmp(apath, apath2)) { | |||
fprintf(stderr, "ERROR: Path %s != %s\n", apath, apath2); | |||
} | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/extfile"), | |||
apath, | |||
strlen(apath) + 1, | |||
map_uri(plugin, LV2_ATOM__Path), | |||
LV2_STATE_IS_PORTABLE); | |||
free(apath); | |||
free(apath2); | |||
if (plugin->rec_file) { | |||
fflush(plugin->rec_file); | |||
apath = map_path->abstract_path(map_path->handle, | |||
plugin->rec_file_path); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/recfile"), | |||
apath, | |||
strlen(apath) + 1, | |||
map_uri(plugin, LV2_ATOM__Path), | |||
LV2_STATE_IS_PORTABLE); | |||
free(apath); | |||
} | |||
if (make_path) { | |||
char* spath = make_path->path(make_path->handle, "save"); | |||
FILE* sfile = fopen(spath, "w"); | |||
fprintf(sfile, "save"); | |||
fclose(sfile); | |||
apath = map_path->abstract_path(map_path->handle, spath); | |||
store(callback_data, | |||
map_uri(plugin, "http://example.org/save-file"), | |||
apath, | |||
strlen(apath) + 1, | |||
map_uri(plugin, LV2_ATOM__Path), | |||
LV2_STATE_IS_PORTABLE); | |||
free(apath); | |||
free(spath); | |||
} | |||
} | |||
return LV2_STATE_SUCCESS; | |||
} | |||
static LV2_State_Status | |||
restore(LV2_Handle instance, | |||
LV2_State_Retrieve_Function retrieve, | |||
void* callback_data, | |||
uint32_t flags, | |||
const LV2_Feature* const* features) | |||
{ | |||
Test* plugin = (Test*)instance; | |||
LV2_State_Map_Path* map_path = NULL; | |||
for (int i = 0; features && features[i]; ++i) { | |||
if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { | |||
map_path = (LV2_State_Map_Path*)features[i]->data; | |||
} | |||
} | |||
size_t size; | |||
uint32_t type; | |||
uint32_t valflags; | |||
plugin->num_runs = *(int32_t*)retrieve( | |||
callback_data, | |||
map_uri(plugin, "http://example.org/num-runs"), | |||
&size, &type, &valflags); | |||
if (!map_path) { | |||
return LV2_STATE_ERR_NO_FEATURE; | |||
} | |||
char* apath = (char*)retrieve( | |||
callback_data, | |||
map_uri(plugin, "http://example.org/extfile"), | |||
&size, &type, &valflags); | |||
if (apath) { | |||
char* path = map_path->absolute_path(map_path->handle, apath); | |||
FILE* f = fopen(path, "r"); | |||
char str[8]; | |||
size_t n_read = fread(str, 1, sizeof(str), f); | |||
fclose(f); | |||
if (strncmp(str, "Hello\n", n_read)) { | |||
fprintf(stderr, "error: Restored bad file contents `%s' != `Hello'\n", | |||
str); | |||
} | |||
free(path); | |||
} | |||
apath = (char*)retrieve( | |||
callback_data, | |||
map_uri(plugin, "http://example.org/save-file"), | |||
&size, &type, &valflags); | |||
if (apath) { | |||
char* spath = map_path->absolute_path(map_path->handle, apath); | |||
FILE* sfile = fopen(spath, "r"); | |||
if (!sfile) { | |||
fprintf(stderr, "error: Failed to open save file %s\n", spath); | |||
} else { | |||
fclose(sfile); | |||
} | |||
free(spath); | |||
} else { | |||
fprintf(stderr, "error: Failed to restore save file.\n"); | |||
} | |||
return LV2_STATE_SUCCESS; | |||
} | |||
static const void* | |||
extension_data(const char* uri) | |||
{ | |||
static const LV2_State_Interface state = { save, restore }; | |||
if (!strcmp(uri, LV2_STATE__interface)) { | |||
return &state; | |||
} | |||
return NULL; | |||
} | |||
static const LV2_Descriptor descriptor = { | |||
TEST_URI, | |||
instantiate, | |||
connect_port, | |||
NULL, // activate, | |||
run, | |||
NULL, // deactivate, | |||
cleanup, | |||
extension_data | |||
}; | |||
LV2_SYMBOL_EXPORT | |||
const LV2_Descriptor* lv2_descriptor(uint32_t index) | |||
{ | |||
switch (index) { | |||
case 0: | |||
return &descriptor; | |||
default: | |||
return NULL; | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
# Lilv Test Plugin | |||
# Copyright 2011 David Robillard <d@drobilla.net> | |||
# | |||
# Permission to use, copy, modify, and/or distribute this software for any | |||
# purpose with or without fee is hereby granted, provided that the above | |||
# copyright notice and this permission notice appear in all copies. | |||
# | |||
# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
@prefix doap: <http://usefulinc.com/ns/doap#> . | |||
@prefix foaf: <http://xmlns.com/foaf/0.1/> . | |||
@prefix lv2: <http://lv2plug.in/ns/lv2core#> . | |||
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . | |||
<http://example.org/lilv-test-plugin> | |||
a lv2:Plugin ; | |||
doap:name "Lilv Test" ; | |||
doap:license <http://opensource.org/licenses/isc> ; | |||
lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#Mapper> ; | |||
lv2:optionalFeature lv2:hardRTCapable ; | |||
lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ; | |||
lv2:port [ | |||
a lv2:InputPort , | |||
lv2:ControlPort ; | |||
lv2:index 0 ; | |||
lv2:symbol "input" ; | |||
lv2:name "Input" | |||
] , [ | |||
a lv2:OutputPort , | |||
lv2:ControlPort ; | |||
lv2:index 1 ; | |||
lv2:symbol "output" ; | |||
lv2:name "Output" | |||
] . |
@@ -0,0 +1,52 @@ | |||
/* | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
/** | |||
@file bench.h A simple real-time benchmarking API. | |||
*/ | |||
#ifndef BENCH_H | |||
#define BENCH_H | |||
#define _POSIX_C_SOURCE 199309L | |||
#include <time.h> | |||
#include <sys/time.h> | |||
static inline double | |||
bench_elapsed_s(const struct timespec* start, const struct timespec* end) | |||
{ | |||
return ((end->tv_sec - start->tv_sec) | |||
+ ((end->tv_nsec - start->tv_nsec) * 0.000000001)); | |||
} | |||
static inline struct timespec | |||
bench_start() | |||
{ | |||
struct timespec start_t; | |||
clock_gettime(CLOCK_REALTIME, &start_t); | |||
return start_t; | |||
} | |||
static inline double | |||
bench_end(const struct timespec* start_t) | |||
{ | |||
struct timespec end_t; | |||
clock_gettime(CLOCK_REALTIME, &end_t); | |||
return bench_elapsed_s(start_t, &end_t); | |||
} | |||
#endif /* BENCH_H */ |
@@ -0,0 +1,38 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <stdio.h> | |||
#include "lilv/lilv.h" | |||
#include "lilv_config.h" | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
LILV_FOREACH(plugins, p, plugins) { | |||
const LilvPlugin* plugin = lilv_plugins_get(plugins, p); | |||
lilv_plugin_get_class(plugin); | |||
} | |||
lilv_world_free(world); | |||
return 0; | |||
} |
@@ -0,0 +1,59 @@ | |||
# Bash auto-completion script written for lv2info and lv2jack. | |||
# Could be adapted to any other program that takes an | |||
# LV2 plugin URI as parameter. | |||
# Updated for Lilv by David Robillard <d@drobilla.net> on 2012-01-08. | |||
# Written by Lars Luthman <lars.luthman@gmail.com> on 2009-10-12. | |||
# No copyright claimed for this script. Do what you want with it. | |||
# For some reason Bash splits the command line not only at whitespace | |||
# but also at ':' signs before putting the parts into COMP_WORDS. | |||
# Since ':' is used in all URIs, which are what we want to complete, | |||
# we have to put the URI back together before we can complete it | |||
# and then cut off the parts we prepended from the completions. | |||
# It probably breaks in some special cases but for most common uses | |||
# it should work fine. | |||
function _lv2info() { | |||
local uri cur opts w wn raw_reply len type | |||
opts=`lv2ls | xargs -n1 echo -n " "` | |||
# This is the last "word", as split by Bash. | |||
cur="${COMP_WORDS[COMP_CWORD]}" | |||
w="$cur" | |||
# Add the previous word while it or this one is a word break character | |||
for i in `seq $(( $COMP_CWORD - 1 )) -1 1`; do | |||
wn="${COMP_WORDS[i]}" | |||
if expr "$COMP_WORDBREAKS" : ".*$wn" > /dev/null; then | |||
if expr "$COMP_WORDBREAKS" : ".*$w" > /dev/null; then | |||
break | |||
fi | |||
fi | |||
w="$wn" | |||
uri="$w$uri" | |||
done | |||
# Check the length of the words we prepend | |||
len=${#uri} | |||
uri="$uri$cur" | |||
raw_reply="$(compgen -W "${opts}" -- ${uri})" | |||
# If we are listing alternatives, just print the full URIs. | |||
type=`echo $COMP_TYPE | awk '{ printf "%c", $1 }'` | |||
if expr "?!@%" : ".*$type" > /dev/null; then | |||
COMPREPLY=( $raw_reply ) | |||
return 0 | |||
fi | |||
# Otherwise, strip the prepended words from all completion suggestions. | |||
COMPREPLY=() | |||
for i in $raw_reply; do | |||
COMPREPLY=( ${COMPREPLY[@]} ${i:len} ) | |||
done | |||
} | |||
complete -F _lv2info lv2info | |||
# And the same for lv2jack. | |||
complete -F _lv2info lv2jack |
@@ -0,0 +1,226 @@ | |||
/* | |||
Copyright 2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#define _POSIX_C_SOURCE 199309L | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lilv/lilv.h" | |||
#include "lv2/lv2plug.in/ns/ext/atom/atom.h" | |||
#include "lilv_config.h" | |||
#include "bench.h" | |||
#include "uri_table.h" | |||
static LilvNode* atom_AtomPort = NULL; | |||
static LilvNode* atom_Sequence = NULL; | |||
static LilvNode* lv2_AudioPort = NULL; | |||
static LilvNode* lv2_CVPort = NULL; | |||
static LilvNode* lv2_ControlPort = NULL; | |||
static LilvNode* lv2_InputPort = NULL; | |||
static LilvNode* lv2_OutputPort = NULL; | |||
static LilvNode* urid_map = NULL; | |||
static bool full_output = false; | |||
static void | |||
print_version(void) | |||
{ | |||
printf( | |||
"lv2bench (lilv) " LILV_VERSION "\n" | |||
"Copyright 2012 David Robillard <http://drobilla.net>\n" | |||
"License: <http://www.opensource.org/licenses/isc-license>\n" | |||
"This is free software: you are free to change and redistribute it.\n" | |||
"There is NO WARRANTY, to the extent permitted by law.\n"); | |||
} | |||
static void | |||
print_usage(void) | |||
{ | |||
printf("lv2bench - Benchmark all installed and supported LV2 plugins.\n"); | |||
printf("Usage: lv2bench [OPTIONS]\n"); | |||
printf("\n"); | |||
printf(" -b BLOCK_SIZE Specify block size, in audio frames.\n"); | |||
printf(" -f, --full Full plottable output.\n"); | |||
printf(" -h, --help Display this help and exit.\n"); | |||
printf(" -n FRAMES Total number of audio frames to process\n"); | |||
printf(" --version Display version information and exit\n"); | |||
} | |||
static double | |||
bench(const LilvPlugin* p, uint32_t sample_count, uint32_t block_size) | |||
{ | |||
URITable uri_table; | |||
uri_table_init(&uri_table); | |||
LV2_URID_Map map = { &uri_table, uri_table_map }; | |||
LV2_Feature map_feature = { LV2_URID_MAP_URI, &map }; | |||
LV2_URID_Unmap unmap = { &uri_table, uri_table_unmap }; | |||
LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap }; | |||
const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL }; | |||
float* const buf = (float*)calloc(block_size * 2, sizeof(float)); | |||
float* const in = buf; | |||
float* const out = buf + block_size; | |||
if (!buf) { | |||
fprintf(stderr, "Out of memory\n"); | |||
return 0.0; | |||
} | |||
LV2_Atom_Sequence seq = { | |||
{ sizeof(LV2_Atom_Sequence_Body), | |||
uri_table_map(&uri_table, LV2_ATOM__Sequence) }, | |||
{ 0, 0 } }; | |||
const char* uri = lilv_node_as_string(lilv_plugin_get_uri(p)); | |||
LilvNodes* required = lilv_plugin_get_required_features(p); | |||
LILV_FOREACH(nodes, i, required) { | |||
const LilvNode* feature = lilv_nodes_get(required, i); | |||
if (!lilv_node_equals(feature, urid_map)) { | |||
fprintf(stderr, "<%s> requires feature <%s>, skipping\n", | |||
uri, lilv_node_as_uri(feature)); | |||
free(buf); | |||
return 0.0; | |||
} | |||
} | |||
LilvInstance* instance = lilv_plugin_instantiate(p, 48000.0, features); | |||
if (!instance) { | |||
fprintf(stderr, "Failed to instantiate <%s>\n", | |||
lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
free(buf); | |||
return 0.0; | |||
} | |||
float* controls = (float*)calloc( | |||
lilv_plugin_get_num_ports(p), sizeof(float)); | |||
lilv_plugin_get_port_ranges_float(p, NULL, NULL, controls); | |||
const uint32_t n_ports = lilv_plugin_get_num_ports(p); | |||
for (uint32_t index = 0; index < n_ports; ++index) { | |||
const LilvPort* port = lilv_plugin_get_port_by_index(p, index); | |||
if (lilv_port_is_a(p, port, lv2_ControlPort)) { | |||
lilv_instance_connect_port(instance, index, &controls[index]); | |||
} else if (lilv_port_is_a(p, port, lv2_AudioPort) || | |||
lilv_port_is_a(p, port, lv2_CVPort)) { | |||
if (lilv_port_is_a(p, port, lv2_InputPort)) { | |||
lilv_instance_connect_port(instance, index, in); | |||
} else if (lilv_port_is_a(p, port, lv2_OutputPort)) { | |||
lilv_instance_connect_port(instance, index, out); | |||
} else { | |||
fprintf(stderr, "<%s> port %d neither input nor output, skipping\n", | |||
uri, index); | |||
lilv_instance_free(instance); | |||
free(buf); | |||
free(controls); | |||
return 0.0; | |||
} | |||
} else if (lilv_port_is_a(p, port, atom_AtomPort)) { | |||
lilv_instance_connect_port(instance, index, &seq); | |||
} else { | |||
fprintf(stderr, "<%s> port %d has unknown type, skipping\n", | |||
uri, index); | |||
lilv_instance_free(instance); | |||
free(buf); | |||
free(controls); | |||
return 0.0; | |||
} | |||
} | |||
lilv_instance_activate(instance); | |||
struct timespec ts = bench_start(); | |||
for (uint32_t i = 0; i < (sample_count / block_size); ++i) { | |||
lilv_instance_run(instance, block_size); | |||
} | |||
const double elapsed = bench_end(&ts); | |||
lilv_instance_deactivate(instance); | |||
lilv_instance_free(instance); | |||
uri_table_destroy(&uri_table); | |||
if (full_output) { | |||
printf("%d %d ", block_size, sample_count); | |||
} | |||
printf("%lf %s\n", elapsed, uri); | |||
free(buf); | |||
free(controls); | |||
return elapsed; | |||
} | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
uint32_t block_size = 512; | |||
uint32_t sample_count = (1 << 19); | |||
for (int i = 1; i < argc; ++i) { | |||
if (!strcmp(argv[i], "--version")) { | |||
print_version(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "--help")) { | |||
print_usage(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "-f")) { | |||
full_output = true; | |||
} else if (!strcmp(argv[i], "-n") && (i + 1 < argc)) { | |||
sample_count = atoi(argv[++i]); | |||
} else if (!strcmp(argv[i], "-b") && (i + 1 < argc)) { | |||
block_size = atoi(argv[++i]); | |||
} else { | |||
print_usage(); | |||
return 1; | |||
} | |||
} | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); | |||
atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence); | |||
lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort); | |||
lv2_CVPort = lilv_new_uri(world, LV2_CORE__CVPort); | |||
lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort); | |||
lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort); | |||
lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort); | |||
urid_map = lilv_new_uri(world, LV2_URID__map); | |||
if (full_output) { | |||
printf("# Block Samples Time Plugin\n"); | |||
} | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
LILV_FOREACH(plugins, i, plugins) { | |||
bench(lilv_plugins_get(plugins, i), sample_count, block_size); | |||
} | |||
lilv_node_free(urid_map); | |||
lilv_node_free(lv2_OutputPort); | |||
lilv_node_free(lv2_InputPort); | |||
lilv_node_free(lv2_ControlPort); | |||
lilv_node_free(lv2_CVPort); | |||
lilv_node_free(lv2_AudioPort); | |||
lilv_node_free(atom_Sequence); | |||
lilv_node_free(atom_AtomPort); | |||
lilv_world_free(world); | |||
return 0; | |||
} |
@@ -0,0 +1,437 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <float.h> | |||
#include <math.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" | |||
#include "lv2/lv2plug.in/ns/ext/presets/presets.h" | |||
#include "lv2/lv2plug.in/ns/ext/event/event.h" | |||
#include "lilv/lilv.h" | |||
#include "lilv_config.h" | |||
#ifdef _MSC_VER | |||
# define isnan _isnan | |||
#endif | |||
LilvNode* applies_to_pred = NULL; | |||
LilvNode* control_class = NULL; | |||
LilvNode* event_class = NULL; | |||
LilvNode* group_pred = NULL; | |||
LilvNode* label_pred = NULL; | |||
LilvNode* preset_class = NULL; | |||
LilvNode* designation_pred = NULL; | |||
LilvNode* supports_event_pred = NULL; | |||
static void | |||
print_port(const LilvPlugin* p, | |||
uint32_t index, | |||
float* mins, | |||
float* maxes, | |||
float* defaults) | |||
{ | |||
const LilvPort* port = lilv_plugin_get_port_by_index(p, index); | |||
printf("\n\tPort %d:\n", index); | |||
if (!port) { | |||
printf("\t\tERROR: Illegal/nonexistent port\n"); | |||
return; | |||
} | |||
bool first = true; | |||
const LilvNodes* classes = lilv_port_get_classes(p, port); | |||
printf("\t\tType: "); | |||
LILV_FOREACH(nodes, i, classes) { | |||
const LilvNode* value = lilv_nodes_get(classes, i); | |||
if (!first) { | |||
printf("\n\t\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(value)); | |||
first = false; | |||
} | |||
if (lilv_port_is_a(p, port, event_class)) { | |||
LilvNodes* supported = lilv_port_get_value( | |||
p, port, supports_event_pred); | |||
if (lilv_nodes_size(supported) > 0) { | |||
printf("\n\t\tSupported events:\n"); | |||
LILV_FOREACH(nodes, i, supported) { | |||
const LilvNode* value = lilv_nodes_get(supported, i); | |||
printf("\t\t\t%s\n", lilv_node_as_uri(value)); | |||
} | |||
} | |||
lilv_nodes_free(supported); | |||
} | |||
LilvScalePoints* points = lilv_port_get_scale_points(p, port); | |||
if (points) | |||
printf("\n\t\tScale Points:\n"); | |||
LILV_FOREACH(scale_points, i, points) { | |||
const LilvScalePoint* point = lilv_scale_points_get(points, i); | |||
printf("\t\t\t%s = \"%s\"\n", | |||
lilv_node_as_string(lilv_scale_point_get_value(point)), | |||
lilv_node_as_string(lilv_scale_point_get_label(point))); | |||
} | |||
lilv_scale_points_free(points); | |||
const LilvNode* sym = lilv_port_get_symbol(p, port); | |||
printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym)); | |||
LilvNode* name = lilv_port_get_name(p, port); | |||
printf("\t\tName: %s\n", lilv_node_as_string(name)); | |||
lilv_node_free(name); | |||
LilvNodes* groups = lilv_port_get_value(p, port, group_pred); | |||
if (lilv_nodes_size(groups) > 0) { | |||
printf("\t\tGroup: %s\n", | |||
lilv_node_as_string(lilv_nodes_get_first(groups))); | |||
} | |||
lilv_nodes_free(groups); | |||
LilvNodes* designations = lilv_port_get_value(p, port, designation_pred); | |||
if (lilv_nodes_size(designations) > 0) { | |||
printf("\t\tDesignation: %s\n", | |||
lilv_node_as_string(lilv_nodes_get_first(designations))); | |||
} | |||
lilv_nodes_free(designations); | |||
if (lilv_port_is_a(p, port, control_class)) { | |||
if (!isnan(mins[index])) | |||
printf("\t\tMinimum: %f\n", mins[index]); | |||
if (!isnan(mins[index])) | |||
printf("\t\tMaximum: %f\n", maxes[index]); | |||
if (!isnan(mins[index])) | |||
printf("\t\tDefault: %f\n", defaults[index]); | |||
} | |||
LilvNodes* properties = lilv_port_get_properties(p, port); | |||
if (lilv_nodes_size(properties) > 0) | |||
printf("\t\tProperties: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, properties) { | |||
if (!first) { | |||
printf("\t\t "); | |||
} | |||
printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i))); | |||
first = false; | |||
} | |||
if (lilv_nodes_size(properties) > 0) | |||
printf("\n"); | |||
lilv_nodes_free(properties); | |||
} | |||
static void | |||
print_plugin(LilvWorld* world, | |||
const LilvPlugin* p) | |||
{ | |||
LilvNode* val = NULL; | |||
printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
val = lilv_plugin_get_name(p); | |||
if (val) { | |||
printf("\tName: %s\n", lilv_node_as_string(val)); | |||
lilv_node_free(val); | |||
} | |||
const LilvPluginClass* pclass = lilv_plugin_get_class(p); | |||
const LilvNode* class_label = lilv_plugin_class_get_label(pclass); | |||
if (class_label) { | |||
printf("\tClass: %s\n", lilv_node_as_string(class_label)); | |||
} | |||
val = lilv_plugin_get_author_name(p); | |||
if (val) { | |||
printf("\tAuthor: %s\n", lilv_node_as_string(val)); | |||
lilv_node_free(val); | |||
} | |||
val = lilv_plugin_get_author_email(p); | |||
if (val) { | |||
printf("\tAuthor Email: %s\n", lilv_node_as_uri(val)); | |||
lilv_node_free(val); | |||
} | |||
val = lilv_plugin_get_author_homepage(p); | |||
if (val) { | |||
printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val)); | |||
lilv_node_free(val); | |||
} | |||
if (lilv_plugin_has_latency(p)) { | |||
uint32_t latency_port = lilv_plugin_get_latency_port_index(p); | |||
printf("\tHas latency: yes, reported by port %d\n", latency_port); | |||
} else { | |||
printf("\tHas latency: no\n"); | |||
} | |||
printf("\tBundle: %s\n", | |||
lilv_node_as_uri(lilv_plugin_get_bundle_uri(p))); | |||
const LilvNode* binary_uri = lilv_plugin_get_library_uri(p); | |||
if (binary_uri) { | |||
printf("\tBinary: %s\n", | |||
lilv_node_as_uri(lilv_plugin_get_library_uri(p))); | |||
} | |||
LilvUIs* uis = lilv_plugin_get_uis(p); | |||
if (lilv_nodes_size(uis) > 0) { | |||
printf("\tUIs:\n"); | |||
LILV_FOREACH(uis, i, uis) { | |||
const LilvUI* ui = lilv_uis_get(uis, i); | |||
printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); | |||
const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); | |||
const LilvNodes* types = lilv_ui_get_classes(ui); | |||
LILV_FOREACH(nodes, t, types) { | |||
printf("\t\t\tClass: %s\n", | |||
lilv_node_as_uri(lilv_nodes_get(types, t))); | |||
} | |||
if (binary) | |||
printf("\t\t\tBinary: %s\n", binary); | |||
printf("\t\t\tBundle: %s\n", | |||
lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); | |||
} | |||
} | |||
lilv_uis_free(uis); | |||
printf("\tData URIs: "); | |||
const LilvNodes* data_uris = lilv_plugin_get_data_uris(p); | |||
bool first = true; | |||
LILV_FOREACH(nodes, i, data_uris) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i))); | |||
first = false; | |||
} | |||
printf("\n"); | |||
/* Required Features */ | |||
LilvNodes* features = lilv_plugin_get_required_features(p); | |||
if (features) | |||
printf("\tRequired Features: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, features) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); | |||
first = false; | |||
} | |||
if (features) | |||
printf("\n"); | |||
lilv_nodes_free(features); | |||
/* Optional Features */ | |||
features = lilv_plugin_get_optional_features(p); | |||
if (features) | |||
printf("\tOptional Features: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, features) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); | |||
first = false; | |||
} | |||
if (features) | |||
printf("\n"); | |||
lilv_nodes_free(features); | |||
/* Extension Data */ | |||
LilvNodes* data = lilv_plugin_get_extension_data(p); | |||
if (data) | |||
printf("\tExtension Data: "); | |||
first = true; | |||
LILV_FOREACH(nodes, i, data) { | |||
if (!first) { | |||
printf("\n\t "); | |||
} | |||
printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); | |||
first = false; | |||
} | |||
if (data) | |||
printf("\n"); | |||
lilv_nodes_free(data); | |||
/* Presets */ | |||
LilvNodes* presets = lilv_plugin_get_related(p, preset_class); | |||
if (presets) | |||
printf("\tPresets: \n"); | |||
LILV_FOREACH(nodes, i, presets) { | |||
const LilvNode* preset = lilv_nodes_get(presets, i); | |||
lilv_world_load_resource(world, preset); | |||
LilvNodes* titles = lilv_world_find_nodes( | |||
world, preset, label_pred, NULL); | |||
if (titles) { | |||
const LilvNode* title = lilv_nodes_get_first(titles); | |||
printf("\t %s\n", lilv_node_as_string(title)); | |||
lilv_nodes_free(titles); | |||
} else { | |||
fprintf(stderr, "Preset <%s> has no rdfs:label\n", | |||
lilv_node_as_string(lilv_nodes_get(presets, i))); | |||
} | |||
} | |||
lilv_nodes_free(presets); | |||
/* Ports */ | |||
const uint32_t num_ports = lilv_plugin_get_num_ports(p); | |||
float* mins = (float*)calloc(num_ports, sizeof(float)); | |||
float* maxes = (float*)calloc(num_ports, sizeof(float)); | |||
float* defaults = (float*)calloc(num_ports, sizeof(float)); | |||
lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults); | |||
for (uint32_t i = 0; i < num_ports; ++i) | |||
print_port(p, i, mins, maxes, defaults); | |||
free(mins); | |||
free(maxes); | |||
free(defaults); | |||
} | |||
static void | |||
print_version(void) | |||
{ | |||
printf( | |||
"lv2info (lilv) " LILV_VERSION "\n" | |||
"Copyright 2007-2011 David Robillard <http://drobilla.net>\n" | |||
"License: <http://www.opensource.org/licenses/isc-license>\n" | |||
"This is free software: you are free to change and redistribute it.\n" | |||
"There is NO WARRANTY, to the extent permitted by law.\n"); | |||
} | |||
static void | |||
print_usage(void) | |||
{ | |||
printf( | |||
"Usage: lv2info [OPTION]... PLUGIN_URI\n" | |||
"Print information about an installed LV2 plugin.\n\n" | |||
" -p FILE Write Turtle description of plugin to FILE\n" | |||
" -m FILE Add record of plugin to manifest FILE\n" | |||
" --help Display this help and exit\n" | |||
" --version Display version information and exit\n\n" | |||
"For -p and -m, Turtle files are appended to (not overwritten),\n" | |||
"and @prefix directives are only written if the file was empty.\n" | |||
"This allows several plugins to be added to a single file.\n"); | |||
} | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
if (argc == 1) { | |||
print_usage(); | |||
return 1; | |||
} | |||
const char* plugin_file = NULL; | |||
const char* manifest_file = NULL; | |||
const char* plugin_uri = NULL; | |||
for (int i = 1; i < argc; ++i) { | |||
if (!strcmp(argv[i], "--version")) { | |||
print_version(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "--help")) { | |||
print_usage(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "-p")) { | |||
plugin_file = argv[++i]; | |||
} else if (!strcmp(argv[i], "-m")) { | |||
manifest_file = argv[++i]; | |||
} else if (argv[i][0] == '-') { | |||
print_usage(); | |||
return 1; | |||
} else if (i == argc - 1) { | |||
plugin_uri = argv[i]; | |||
} | |||
} | |||
int ret = 0; | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
LilvNode* uri = lilv_new_uri(world, plugin_uri); | |||
if (!uri) { | |||
fprintf(stderr, "Invalid plugin URI\n"); | |||
lilv_world_free(world); | |||
return 1; | |||
} | |||
applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo); | |||
control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); | |||
event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); | |||
group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group); | |||
label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); | |||
preset_class = lilv_new_uri(world, LV2_PRESETS__Preset); | |||
designation_pred = lilv_new_uri(world, LV2_CORE__designation); | |||
supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent); | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri); | |||
if (p && plugin_file) { | |||
LilvNode* base = lilv_new_uri(world, plugin_file); | |||
FILE* plugin_fd = fopen(plugin_file, "a"); | |||
lilv_plugin_write_description(world, p, base, plugin_fd); | |||
fclose(plugin_fd); | |||
if (manifest_file) { | |||
FILE* manifest_fd = fopen(manifest_file, "a"); | |||
lilv_plugin_write_manifest_entry( | |||
world, p, base, manifest_fd, plugin_file); | |||
fclose(manifest_fd); | |||
} | |||
lilv_node_free(base); | |||
} else if (p) { | |||
print_plugin(world, p); | |||
} else { | |||
fprintf(stderr, "Plugin not found.\n"); | |||
} | |||
ret = (p != NULL ? 0 : -1); | |||
lilv_node_free(uri); | |||
lilv_node_free(supports_event_pred); | |||
lilv_node_free(designation_pred); | |||
lilv_node_free(preset_class); | |||
lilv_node_free(label_pred); | |||
lilv_node_free(group_pred); | |||
lilv_node_free(event_class); | |||
lilv_node_free(control_class); | |||
lilv_node_free(applies_to_pred); | |||
lilv_world_free(world); | |||
return ret; | |||
} | |||
@@ -0,0 +1,93 @@ | |||
/* | |||
Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include "lilv/lilv.h" | |||
#include "lilv_config.h" | |||
static void | |||
list_plugins(const LilvPlugins* list, bool show_names) | |||
{ | |||
LILV_FOREACH(plugins, i, list) { | |||
const LilvPlugin* p = lilv_plugins_get(list, i); | |||
if (show_names) { | |||
LilvNode* n = lilv_plugin_get_name(p); | |||
printf("%s\n", lilv_node_as_string(n)); | |||
lilv_node_free(n); | |||
} else { | |||
printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
} | |||
} | |||
} | |||
static void | |||
print_version(void) | |||
{ | |||
printf( | |||
"lv2ls (lilv) " LILV_VERSION "\n" | |||
"Copyright 2007-2011 David Robillard <http://drobilla.net>\n" | |||
"License: <http://www.opensource.org/licenses/isc-license>\n" | |||
"This is free software: you are free to change and redistribute it.\n" | |||
"There is NO WARRANTY, to the extent permitted by law.\n"); | |||
} | |||
static void | |||
print_usage(void) | |||
{ | |||
printf("Usage: lv2ls [OPTION]...\n"); | |||
printf("List all installed LV2 plugins.\n"); | |||
printf("\n"); | |||
printf(" -n, --names Show names instead of URIs\n"); | |||
printf(" --help Display this help and exit\n"); | |||
printf(" --version Display version information and exit\n"); | |||
printf("\n"); | |||
printf("The environment variable LV2_PATH can be used to control where\n"); | |||
printf("this (and all other lilv based LV2 hosts) will search for plugins.\n"); | |||
} | |||
int | |||
main(int argc, char** argv) | |||
{ | |||
bool show_names = false; | |||
for (int i = 1; i < argc; ++i) { | |||
if (!strcmp(argv[i], "--names") || !strcmp(argv[i], "-n")) { | |||
show_names = true; | |||
} else if (!strcmp(argv[i], "--version")) { | |||
print_version(); | |||
return 0; | |||
} else if (!strcmp(argv[i], "--help")) { | |||
print_usage(); | |||
return 0; | |||
} else { | |||
print_usage(); | |||
return 1; | |||
} | |||
} | |||
LilvWorld* world = lilv_world_new(); | |||
lilv_world_load_all(world); | |||
const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
list_plugins(plugins, show_names); | |||
lilv_world_free(world); | |||
return 0; | |||
} |
@@ -0,0 +1,73 @@ | |||
/* | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
/** | |||
@file uri_table.h A toy URI map/unmap implementation. | |||
This file contains function definitions and must only be included once. | |||
*/ | |||
#ifndef URI_TABLE_H | |||
#define URI_TABLE_H | |||
typedef struct { | |||
char** uris; | |||
size_t n_uris; | |||
} URITable; | |||
static void | |||
uri_table_init(URITable* table) | |||
{ | |||
table->uris = NULL; | |||
table->n_uris = 0; | |||
} | |||
static void | |||
uri_table_destroy(URITable* table) | |||
{ | |||
free(table->uris); | |||
} | |||
static LV2_URID | |||
uri_table_map(LV2_URID_Map_Handle handle, | |||
const char* uri) | |||
{ | |||
URITable* table = (URITable*)handle; | |||
for (size_t i = 0; i < table->n_uris; ++i) { | |||
if (!strcmp(table->uris[i], uri)) { | |||
return i + 1; | |||
} | |||
} | |||
const size_t len = strlen(uri); | |||
table->uris = (char**)realloc(table->uris, ++table->n_uris * sizeof(char*)); | |||
table->uris[table->n_uris - 1] = malloc(len + 1); | |||
memcpy(table->uris[table->n_uris - 1], uri, len + 1); | |||
return table->n_uris; | |||
} | |||
static const char* | |||
uri_table_unmap(LV2_URID_Map_Handle handle, | |||
LV2_URID urid) | |||
{ | |||
URITable* table = (URITable*)handle; | |||
if (urid > 0 && urid <= table->n_uris) { | |||
return table->uris[urid - 1]; | |||
} | |||
return NULL; | |||
} | |||
#endif /* URI_TABLE_H */ |
@@ -0,0 +1,378 @@ | |||
#!/usr/bin/env python | |||
import os | |||
import sys | |||
import subprocess | |||
from waflib.extras import autowaf as autowaf | |||
import waflib.Options as Options | |||
# Version of this package (even if built as a child) | |||
LILV_VERSION = '0.14.4' | |||
LILV_MAJOR_VERSION = '0' | |||
# Library version (UNIX style major, minor, micro) | |||
# major increment <=> incompatible changes | |||
# minor increment <=> compatible changes (additions) | |||
# micro increment <=> no interface changes | |||
# Lilv uses the same version number for both library and package | |||
LILV_LIB_VERSION = LILV_VERSION | |||
# Variables for 'waf dist' | |||
APPNAME = 'lilv' | |||
VERSION = LILV_VERSION | |||
# Mandatory variables | |||
top = '.' | |||
out = 'build' | |||
def options(opt): | |||
opt.load('compiler_c') | |||
opt.load('compiler_cxx') | |||
opt.load('python') | |||
autowaf.set_options(opt) | |||
opt.add_option('--no-utils', action='store_true', dest='no_utils', | |||
help='Do not build command line utilities') | |||
opt.add_option('--bindings', action='store_true', dest='bindings', | |||
help='Build python bindings') | |||
opt.add_option('--dyn-manifest', action='store_true', dest='dyn_manifest', | |||
help='Build support for dynamic manifests') | |||
opt.add_option('--test', action='store_true', dest='build_tests', | |||
help='Build unit tests') | |||
opt.add_option('--no-bash-completion', action='store_true', | |||
dest='no_bash_completion', | |||
help='Do not install bash completion script in CONFIGDIR') | |||
opt.add_option('--static', action='store_true', dest='static', | |||
help='Build static library') | |||
opt.add_option('--no-shared', action='store_true', dest='no_shared', | |||
help='Do not build shared library') | |||
opt.add_option('--static-progs', action='store_true', dest='static_progs', | |||
help='Build programs as static binaries') | |||
opt.add_option('--default-lv2-path', type='string', default='', | |||
dest='default_lv2_path', | |||
help='Default LV2 path to use if LV2_PATH is unset') | |||
def configure(conf): | |||
conf.load('compiler_c') | |||
if Options.options.bindings: | |||
try: | |||
conf.load('swig') | |||
conf.load('python') | |||
conf.load('compiler_cxx') | |||
conf.check_python_headers() | |||
autowaf.define(conf, 'LILV_PYTHON', 1); | |||
except: | |||
pass | |||
autowaf.configure(conf) | |||
autowaf.set_c99_mode(conf) | |||
autowaf.display_header('Lilv Configuration') | |||
conf.env.BASH_COMPLETION = not Options.options.no_bash_completion | |||
conf.env.BUILD_TESTS = Options.options.build_tests | |||
conf.env.BUILD_UTILS = not Options.options.no_utils | |||
conf.env.BUILD_SHARED = not Options.options.no_shared | |||
conf.env.STATIC_PROGS = Options.options.static_progs | |||
conf.env.BUILD_STATIC = (Options.options.static or | |||
Options.options.static_progs) | |||
if not conf.env.BUILD_SHARED and not conf.env.BUILD_STATIC: | |||
conf.fatal('Neither a shared nor a static build requested') | |||
autowaf.check_pkg(conf, 'lv2', uselib_store='LV2', | |||
atleast_version='1.0.0', mandatory=True) | |||
autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', | |||
atleast_version='0.14.0', mandatory=True) | |||
autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD', | |||
atleast_version='0.8.0', mandatory=True) | |||
autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM', | |||
atleast_version='0.2.0', mandatory=True) | |||
autowaf.define(conf, 'LILV_NEW_LV2', 1) # New LV2 discovery API | |||
defines = ['_POSIX_C_SOURCE', '_BSD_SOURCE'] | |||
if Options.platform == 'darwin': | |||
defines += ['_DARWIN_C_SOURCE'] | |||
# Check for gcov library (for test coverage) | |||
if conf.env.BUILD_TESTS: | |||
conf.check_cc(lib='gcov', | |||
define_name='HAVE_GCOV', | |||
mandatory=False) | |||
conf.check_cc(function_name='flock', | |||
header_name='sys/file.h', | |||
defines=defines, | |||
define_name='HAVE_FLOCK', | |||
mandatory=False) | |||
conf.check_cc(function_name='fileno', | |||
header_name='stdio.h', | |||
defines=defines, | |||
define_name='HAVE_FILENO', | |||
mandatory=False) | |||
conf.check_cc(function_name='clock_gettime', | |||
header_name=['sys/time.h','time.h'], | |||
defines=['_POSIX_C_SOURCE=199309L'], | |||
define_name='HAVE_CLOCK_GETTIME', | |||
uselib_store='CLOCK_GETTIME', | |||
lib=['rt'], | |||
mandatory=False) | |||
autowaf.define(conf, 'LILV_VERSION', LILV_VERSION) | |||
if Options.options.dyn_manifest: | |||
autowaf.define(conf, 'LILV_DYN_MANIFEST', 1) | |||
lilv_path_sep = ':' | |||
lilv_dir_sep = '/' | |||
if sys.platform == 'win32': | |||
lilv_path_sep = ';' | |||
lilv_dir_sep = '\\\\' | |||
autowaf.define(conf, 'LILV_PATH_SEP', lilv_path_sep) | |||
autowaf.define(conf, 'LILV_DIR_SEP', lilv_dir_sep) | |||
# Set default LV2 path | |||
lv2_path = Options.options.default_lv2_path | |||
if lv2_path == '': | |||
if Options.platform == 'darwin': | |||
lv2_path = lilv_path_sep.join(['~/Library/Audio/Plug-Ins/LV2', | |||
'~/.lv2', | |||
'/usr/local/lib/lv2', | |||
'/usr/lib/lv2', | |||
'/Library/Audio/Plug-Ins/LV2']) | |||
elif Options.platform == 'haiku': | |||
lv2_path = lilv_path_sep.join(['~/.lv2', | |||
'/boot/common/add-ons/lv2']) | |||
elif Options.platform == 'win32': | |||
lv2_path = lilv_path_sep.join(['%APPDATA%\\\\LV2', | |||
'%COMMONPROGRAMFILES%\\\\LV2']) | |||
else: | |||
libdirname = os.path.basename(conf.env.LIBDIR) | |||
lv2_path = lilv_path_sep.join(['~/.lv2', | |||
'/usr/%s/lv2' % libdirname, | |||
'/usr/local/%s/lv2' % libdirname]) | |||
autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', lv2_path) | |||
conf.env.LIB_LILV = ['lilv-%s' % LILV_MAJOR_VERSION] | |||
conf.write_config_header('lilv_config.h', remove=False) | |||
autowaf.display_msg(conf, 'Default LV2_PATH', | |||
conf.env.LILV_DEFAULT_LV2_PATH) | |||
autowaf.display_msg(conf, 'Utilities', | |||
bool(conf.env.BUILD_UTILS)) | |||
autowaf.display_msg(conf, 'Unit tests', | |||
bool(conf.env.BUILD_TESTS)) | |||
autowaf.display_msg(conf, 'Dynamic manifest support', | |||
bool(conf.env.LILV_DYN_MANIFEST)) | |||
autowaf.display_msg(conf, 'Python bindings', | |||
conf.is_defined('LILV_PYTHON')) | |||
conf.undefine('LILV_DEFAULT_LV2_PATH') # Cmd line errors with VC++ | |||
print('') | |||
def build_util(bld, name, defines): | |||
obj = bld(features = 'c cprogram', | |||
source = name + '.c', | |||
includes = ['.', './src', './utils'], | |||
use = 'liblilv', | |||
target = name, | |||
defines = defines, | |||
install_path = '${BINDIR}') | |||
if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS: | |||
obj.use = 'liblilv_static' | |||
if bld.env.STATIC_PROGS: | |||
if not bld.env.MSVC_COMPILER: | |||
obj.lib = ['m'] | |||
obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER | |||
obj.linkflags = ['-static', '-Wl,--start-group'] | |||
return obj | |||
def build(bld): | |||
# C/C++ Headers | |||
includedir = '${INCLUDEDIR}/lilv-%s/lilv' % LILV_MAJOR_VERSION | |||
bld.install_files(includedir, bld.path.ant_glob('lilv/*.h')) | |||
bld.install_files(includedir, bld.path.ant_glob('lilv/*.hpp')) | |||
# Pkgconfig file | |||
autowaf.build_pc(bld, 'LILV', LILV_VERSION, LILV_MAJOR_VERSION, [], | |||
{'LILV_MAJOR_VERSION' : LILV_MAJOR_VERSION, | |||
'LILV_PKG_DEPS' : 'lv2 serd-0 sord-0 sratom-0'}) | |||
lib_source = ''' | |||
src/collections.c | |||
src/instance.c | |||
src/lib.c | |||
src/node.c | |||
src/plugin.c | |||
src/pluginclass.c | |||
src/port.c | |||
src/query.c | |||
src/scalepoint.c | |||
src/state.c | |||
src/ui.c | |||
src/util.c | |||
src/world.c | |||
src/zix/tree.c | |||
'''.split() | |||
lib = ['dl'] | |||
libflags = ['-fvisibility=hidden'] | |||
defines = [] | |||
if sys.platform == 'win32': | |||
lib = [] | |||
if bld.env.MSVC_COMPILER: | |||
libflags = [] | |||
defines = ['snprintf=_snprintf'] | |||
elif sys.platform.find('bsd') > 0: | |||
lib = [] | |||
# Shared Library | |||
if bld.env.BUILD_SHARED: | |||
obj = bld(features = 'c cshlib', | |||
export_includes = ['.'], | |||
source = lib_source, | |||
includes = ['.', './src'], | |||
name = 'liblilv', | |||
target = 'lilv-%s' % LILV_MAJOR_VERSION, | |||
vnum = LILV_LIB_VERSION, | |||
install_path = '${LIBDIR}', | |||
defines = ['LILV_SHARED', 'LILV_INTERNAL'], | |||
cflags = libflags, | |||
lib = lib) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
# Static library | |||
if bld.env.BUILD_STATIC: | |||
obj = bld(features = 'c cstlib', | |||
export_includes = ['.'], | |||
source = lib_source, | |||
includes = ['.', './src'], | |||
name = 'liblilv_static', | |||
target = 'lilv-%s' % LILV_MAJOR_VERSION, | |||
vnum = LILV_LIB_VERSION, | |||
install_path = '${LIBDIR}', | |||
defines = defines + ['LILV_INTERNAL']) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
if bld.env.BUILD_TESTS: | |||
test_libs = lib | |||
test_cflags = [''] | |||
if bld.is_defined('HAVE_GCOV'): | |||
test_libs += ['gcov'] | |||
test_cflags += ['-fprofile-arcs', '-ftest-coverage'] | |||
# Test plugin library | |||
penv = bld.env.derive() | |||
shlib_pattern = penv.cshlib_PATTERN | |||
if shlib_pattern.startswith('lib'): | |||
shlib_pattern = shlib_pattern[3:] | |||
penv.cshlib_PATTERN = shlib_pattern | |||
shlib_ext = shlib_pattern[shlib_pattern.rfind('.'):] | |||
obj = bld(features = 'c cshlib', | |||
env = penv, | |||
source = 'test/test_plugin.c', | |||
name = 'test_plugin', | |||
target = 'test/test_plugin.lv2/test_plugin', | |||
install_path = None, | |||
defines = defines, | |||
cflags = test_cflags, | |||
lib = test_libs, | |||
uselib = 'LV2') | |||
# Test plugin data files | |||
for i in [ 'manifest.ttl.in', 'test_plugin.ttl.in' ]: | |||
bld(features = 'subst', | |||
source = 'test/' + i, | |||
target = 'test/test_plugin.lv2/' + i.replace('.in', ''), | |||
install_path = None, | |||
SHLIB_EXT = shlib_ext) | |||
# Static profiled library (for unit test code coverage) | |||
obj = bld(features = 'c cstlib', | |||
source = lib_source, | |||
includes = ['.', './src'], | |||
name = 'liblilv_profiled', | |||
target = 'lilv_profiled', | |||
install_path = None, | |||
defines = defines + ['LILV_INTERNAL'], | |||
cflags = test_cflags, | |||
lib = test_libs) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
# Unit test program | |||
bpath = os.path.abspath(os.path.join(out, 'test', 'test_plugin.lv2')) | |||
bpath = bpath.replace('\\', '/') | |||
obj = bld(features = 'c cprogram', | |||
source = 'test/lilv_test.c', | |||
includes = ['.', './src'], | |||
use = 'liblilv_profiled', | |||
uselib = 'SORD LV2', | |||
lib = test_libs, | |||
target = 'test/lilv_test', | |||
install_path = None, | |||
defines = defines + ['LILV_TEST_BUNDLE=\"%s/\"' % bpath], | |||
cflags = test_cflags) | |||
autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
# Utilities | |||
if bld.env.BUILD_UTILS: | |||
utils = ''' | |||
utils/lilv-bench | |||
utils/lv2info | |||
utils/lv2ls | |||
''' | |||
for i in utils.split(): | |||
build_util(bld, i, defines) | |||
# lv2bench (less portable than other utilities) | |||
if bld.is_defined('HAVE_CLOCK_GETTIME'): | |||
obj = build_util(bld, 'utils/lv2bench', defines) | |||
if not bld.env.MSVC_COMPILER: | |||
obj.lib = ['rt'] | |||
# Documentation | |||
autowaf.build_dox(bld, 'LILV', LILV_VERSION, top, out) | |||
# Man pages | |||
bld.install_files('${MANDIR}/man1', bld.path.ant_glob('doc/*.1')) | |||
# Bash completion | |||
if bld.env.BASH_COMPLETION: | |||
bld.install_as( | |||
'${SYSCONFDIR}/bash_completion.d/lilv', 'utils/lilv.bash_completion') | |||
if bld.is_defined('LILV_PYTHON'): | |||
# Python Wrapper | |||
obj = bld(features = 'cxx cxxshlib pyext', | |||
source = 'bindings/lilv.i', | |||
target = 'bindings/_lilv', | |||
includes = ['..'], | |||
swig_flags = '-c++ -python -Wall -I.. -llilv -features autodoc=1', | |||
use = 'liblilv') | |||
autowaf.use_lib(bld, obj, 'LILV') | |||
bld.install_files('${PYTHONDIR}', 'bindings/lilv.py') | |||
bld.add_post_fun(autowaf.run_ldconfig) | |||
if bld.env.DOCS: | |||
bld.add_post_fun(fix_docs) | |||
def fix_docs(ctx): | |||
if ctx.cmd == 'build': | |||
autowaf.make_simple_dox(APPNAME) | |||
def upload_docs(ctx): | |||
os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/lilv/') | |||
def test(ctx): | |||
autowaf.pre_test(ctx, APPNAME) | |||
os.environ['PATH'] = 'test' + os.pathsep + os.getenv('PATH') | |||
autowaf.run_tests(ctx, APPNAME, ['lilv_test'], dirs=['./src','./test']) | |||
autowaf.post_test(ctx, APPNAME) | |||
def lint(ctx): | |||
subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include,-runtime/sizeof src/* lilv/*', shell=True) |
@@ -0,0 +1,31 @@ | |||
/* | |||
* Carla static lilv code | |||
* Copyright (C) 2012 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the COPYING file | |||
*/ | |||
#include "src/collections.c" | |||
#include "src/instance.c" | |||
#include "src/lib.c" | |||
#include "src/node.c" | |||
#include "src/plugin.c" | |||
#include "src/pluginclass.c" | |||
#include "src/port.c" | |||
#include "src/query.c" | |||
#include "src/scalepoint.c" | |||
#include "src/state.c" | |||
#include "src/ui.c" | |||
#include "src/util.c" | |||
#include "src/world.c" | |||
#include "src/zix/tree.c" |
@@ -0,0 +1 @@ | |||
David Robillard <d@drobilla.net> |
@@ -0,0 +1,13 @@ | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
@@ -0,0 +1,59 @@ | |||
Installation Instructions | |||
========================= | |||
Basic Installation | |||
------------------ | |||
Building this software requires only Python. To install with default options: | |||
./waf configure | |||
./waf | |||
./waf install | |||
You may need to become root for the install stage, for example: | |||
sudo ./waf install | |||
Configuration Options | |||
--------------------- | |||
All supported options can be viewed using the command: | |||
./waf --help | |||
Most options only need to be passed during the configure stage, for example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install | |||
Compiler Configuration | |||
---------------------- | |||
Several standard environment variables can be used to control how compilers are | |||
invoked: | |||
* CC: Path to C compiler | |||
* CFLAGS: C compiler options | |||
* CXX: Path to C++ compiler | |||
* CXXFLAGS: C++ compiler options | |||
* CPPFLAGS: C preprocessor options | |||
* LINKFLAGS: Linker options | |||
Installation Directories | |||
------------------------ | |||
The --prefix option (or the PREFIX environment variable) can be used to change | |||
the prefix which all files are installed under. There are also several options | |||
allowing for more fine-tuned control, see the --help output for details. | |||
Packaging | |||
--------- | |||
Everything can be installed to a specific root directory by passing a --destdir | |||
option to the install stage (or setting the DESTDIR environment variable), | |||
which adds a prefix to all install paths. For example: | |||
./waf configure --prefix=/usr | |||
./waf | |||
./waf install --destdir=/tmp/package |
@@ -0,0 +1,98 @@ | |||
serd (0.18.2) stable; | |||
* Fix crash when serd_node_new_decimal is called with infinity or NaN | |||
* Fix crash when resolving against non-standard base URIs | |||
* Fix bug that caused "a" abbreviation in non-predicate position | |||
* Disable timestamps in HTML documentation for reproducible build | |||
* Fix clashing symbol "error" in amalgamation build | |||
* Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory) | |||
-- David Robillard <d@drobilla.net> Sat, 22 Dec 2012 21:32:15 -0500 | |||
serd (0.18.0) stable; | |||
* Support digits at start of local names as per new Turtle grammar | |||
* Add incremental read interface suitable for reading from infinite streams | |||
* Add -e option to serdi to use incremental reading | |||
* Add error callback to reader and writer for custom error reporting | |||
* Add -q option to serdi to suppress all non-data output, e.g. errors | |||
* Reset indent when finishing a write | |||
* Report write size correctly when invalid UTF-8 is encountered and a | |||
replacement character is written | |||
* Strip down API documentation to a single clean page | |||
* Fix various hyper-strict warnings | |||
* Do not require a C++ compiler to build | |||
* Add option to build utilities as static binaries | |||
* Upgrade to waf 1.7.2 | |||
-- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 00:18:34 -0400 | |||
serd (0.14.0) stable; | |||
* Use path variables in pkgconfig files | |||
* Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||
* Tolerate invalid characters in string literals by replacing with the | |||
Unicode replacement character | |||
* Report reason for failure to open file in serdi | |||
* Improve write performance by doing bulk writes for unescaped substrings | |||
* Add SerdBulkSink for writing bulk output and corresponding serdi -B option | |||
* Add serdi -f option to prevent URI qualification | |||
* Remove use of multi-byte peek (readahead) and use exactly 1 page for | |||
read buffer (instead of 2) | |||
* Handle a quote as the last character of a long string literal in the | |||
writer (by escaping it) rather than the reader, to avoid writing Turtle | |||
other tools fail to parse. | |||
* Add serd_strtod(), serd_node_new_decimal(), and serd_node_new_integer() | |||
for locale-independent numeric node parsing/serialising. | |||
* Add serd_file_sink for easy writing to a FILE* stream. | |||
* Add serd_chunk_sink for easy writing to a string. | |||
* Escape ASCII control characters in output (e.g. fix problems with string | |||
literals that start with a backspace) | |||
* Improve URI resolution to cover most of the abnormal cases from RFC3986 | |||
* Support file://localhost/foo URIs in serd_uri_to_path() | |||
* Support Windows file://c:/foo URIs in serd_uri_to_path() on all platforms | |||
* Add serd_node_new_blob and serd_base64_decode for handling arbitrary | |||
binary data via base64 encoding. | |||
* Support compilation as C++ under MSVC++. | |||
* Implement pretty-printing for collections. | |||
* Parse collections iteratively in O(1) space. | |||
* Report read error if both "genid" and "docid" IDs are found in the same | |||
document, to prevent silent merging of distinct blank nodes. | |||
* Handle files and strings that start with a UTF-8 Byte Order Mark. | |||
* Add serd_writer_get_env(). | |||
* Add serd_node_new_file_uri() and serd_file_uri_parse() and implement | |||
proper URI to/from path hex escaping, etc. | |||
* Add serd_uri_serialise_relative() for making URIs relative to a base | |||
where possible (by chopping a common prefix and adding dot segments). | |||
* Make URIs serialised by the writer properly escape characters. | |||
* Add serd_writer_set_root_uri() and corresponding -r option to serdi to | |||
enable writing URIs with up references (../). | |||
* Resolve dot segments in serd_uri_resolve() instead of at write time. | |||
* Add serd_reader_set_default_graph() for reading a file as a named graph. | |||
-- David Robillard <d@drobilla.net> Tue, 17 Apr 2012 18:23:53 -0400 | |||
serd (0.5.0) stable; | |||
* Fix pretty printing of successive blank descriptions, i.e. "] , [" | |||
* Avoid writing illegal Turtle names as a result of URI qualifying | |||
* Gracefully handle NULL reader sinks | |||
* Add serd_strerror | |||
* Add serd_env_set_prefix_from_strings for convenience | |||
* Fix erroneously equal SERD_ERR_BAD_SYNTAX and SERD_ERR_BAD_ARG | |||
* Add ability to build static library | |||
-- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||
serd (0.4.2) stable; | |||
* Fix compilation issues on some systems | |||
* Fix build system Python 3 compatibility | |||
-- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||
serd (0.4.0) stable; | |||
* Initial release | |||
-- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 |
@@ -0,0 +1,29 @@ | |||
This library is designed to allow parallel installation of different major | |||
versions. To facilitate this, the shared library name, include directory, and | |||
pkg-config file are suffixed with the major version number of the library. | |||
For example, if this library was named "foo" and at version 1.x.y: | |||
/usr/include/foo-1/foo/foo.h | |||
/usr/lib/foo-1.so.1.x.y | |||
/usr/lib/pkgconfig/foo-1.pc | |||
Dependencies check for pkg-config name "foo-1" and will build | |||
against a compatible version 1, regardless any other installed versions. | |||
*** IMPORTANT GUIDELINES FOR PACKAGERS *** | |||
Packages should follow the same conventions as above, i.e. include the major | |||
version (and only the major version) in the name of the package. Continuing the | |||
example above, the package(s) would be named foo-1 and foo-1-dev. This way, | |||
if/when version 2 comes out, it may be installed at the same time as version 1 | |||
without breaking anything. | |||
Please do not create packages of this library that do not follow these | |||
guidelines, you will break things and cause unnecessary headaches. Please do | |||
not use any number as a suffix other than the actual major version number of the | |||
upstream source package. | |||
Because program and documentation names are not versioned, these should be | |||
included in separate packages which may replace previous versions, since | |||
there is little use in having parallel installations of them. |
@@ -0,0 +1,10 @@ | |||
Serd | |||
---- | |||
Serd is a lightweight C library for RDF syntax which supports reading and | |||
writing Turtle and NTriples. | |||
For more information, see <http://drobilla.net/software/serd>. | |||
-- David Robillard <d@drobilla.net> | |||
@@ -0,0 +1,187 @@ | |||
<doxygenlayout version="1.0"> | |||
<!-- Navigation index tabs for HTML output --> | |||
<navindex> | |||
<tab type="mainpage" visible="yes" title=""/> | |||
<tab type="pages" visible="yes" title="" intro=""/> | |||
<tab type="modules" visible="yes" title="" intro=""/> | |||
<tab type="namespaces" visible="yes" title=""> | |||
<tab type="namespacelist" visible="yes" title="" intro=""/> | |||
<tab type="namespacemembers" visible="yes" title="" intro=""/> | |||
</tab> | |||
<tab type="classes" visible="yes" title=""> | |||
<tab type="classlist" visible="yes" title="" intro=""/> | |||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/> | |||
<tab type="hierarchy" visible="yes" title="" intro=""/> | |||
<tab type="classmembers" visible="yes" title="" intro=""/> | |||
</tab> | |||
<tab type="files" visible="yes" title=""> | |||
<tab type="filelist" visible="yes" title="" intro=""/> | |||
<tab type="globals" visible="yes" title="" intro=""/> | |||
</tab> | |||
<tab type="examples" visible="yes" title="" intro=""/> | |||
</navindex> | |||
<!-- Layout definition for a class page --> | |||
<class> | |||
<briefdescription visible="yes"/> | |||
<includes visible="$SHOW_INCLUDE_FILES"/> | |||
<inheritancegraph visible="$CLASS_GRAPH"/> | |||
<collaborationgraph visible="$COLLABORATION_GRAPH"/> | |||
<allmemberslink visible="yes"/> | |||
<memberdecl> | |||
<nestedclasses visible="yes" title=""/> | |||
<publictypes title=""/> | |||
<publicslots title=""/> | |||
<signals title=""/> | |||
<publicmethods title=""/> | |||
<publicstaticmethods title=""/> | |||
<publicattributes title=""/> | |||
<publicstaticattributes title=""/> | |||
<protectedtypes title=""/> | |||
<protectedslots title=""/> | |||
<protectedmethods title=""/> | |||
<protectedstaticmethods title=""/> | |||
<protectedattributes title=""/> | |||
<protectedstaticattributes title=""/> | |||
<packagetypes title=""/> | |||
<packagemethods title=""/> | |||
<packagestaticmethods title=""/> | |||
<packageattributes title=""/> | |||
<packagestaticattributes title=""/> | |||
<properties title=""/> | |||
<events title=""/> | |||
<privatetypes title=""/> | |||
<privateslots title=""/> | |||
<privatemethods title=""/> | |||
<privatestaticmethods title=""/> | |||
<privateattributes title=""/> | |||
<privatestaticattributes title=""/> | |||
<friends title=""/> | |||
<related title="" subtitle=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
<memberdef> | |||
<inlineclasses title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<constructors title=""/> | |||
<functions title=""/> | |||
<related title=""/> | |||
<variables title=""/> | |||
<properties title=""/> | |||
<events title=""/> | |||
</memberdef> | |||
<usedfiles visible="$SHOW_USED_FILES"/> | |||
<authorsection visible="yes"/> | |||
</class> | |||
<!-- Layout definition for a namespace page --> | |||
<namespace> | |||
<briefdescription visible="yes"/> | |||
<memberdecl> | |||
<nestednamespaces visible="yes" title=""/> | |||
<classes visible="yes" title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
<memberdef> | |||
<inlineclasses title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
</memberdef> | |||
<authorsection visible="yes"/> | |||
</namespace> | |||
<!-- Layout definition for a file page --> | |||
<file> | |||
<briefdescription visible="yes"/> | |||
<includes visible="$SHOW_INCLUDE_FILES"/> | |||
<includegraph visible="$INCLUDE_GRAPH"/> | |||
<includedbygraph visible="$INCLUDED_BY_GRAPH"/> | |||
<sourcelink visible="yes"/> | |||
<memberdecl> | |||
<classes visible="yes" title=""/> | |||
<namespaces visible="yes" title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
<memberdef> | |||
<inlineclasses title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
</memberdef> | |||
<authorsection/> | |||
</file> | |||
<!-- Layout definition for a group page --> | |||
<group> | |||
<briefdescription visible="no"/> | |||
<groupgraph visible="$GROUP_GRAPHS"/> | |||
<detaileddescription title=""/> | |||
<memberdecl> | |||
<nestedgroups visible="yes" title=""/> | |||
<dirs visible="yes" title=""/> | |||
<files visible="yes" title=""/> | |||
<namespaces visible="yes" title=""/> | |||
<classes visible="yes" title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<enumvalues title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<signals title=""/> | |||
<publicslots title=""/> | |||
<protectedslots title=""/> | |||
<privateslots title=""/> | |||
<events title=""/> | |||
<properties title=""/> | |||
<friends title=""/> | |||
<membergroups visible="yes"/> | |||
</memberdecl> | |||
<memberdef> | |||
<pagedocs/> | |||
<inlineclasses title=""/> | |||
<defines title=""/> | |||
<typedefs title=""/> | |||
<enums title=""/> | |||
<enumvalues title=""/> | |||
<functions title=""/> | |||
<variables title=""/> | |||
<signals title=""/> | |||
<publicslots title=""/> | |||
<protectedslots title=""/> | |||
<privateslots title=""/> | |||
<events title=""/> | |||
<properties title=""/> | |||
<friends title=""/> | |||
</memberdef> | |||
<authorsection visible="yes"/> | |||
</group> | |||
<!-- Layout definition for a directory page --> | |||
<directory> | |||
<briefdescription visible="yes"/> | |||
<directorygraph visible="yes"/> | |||
<memberdecl> | |||
<dirs visible="yes"/> | |||
<files visible="yes"/> | |||
</memberdecl> | |||
<detaileddescription title=""/> | |||
</directory> | |||
</doxygenlayout> |
@@ -0,0 +1,72 @@ | |||
.TH SERDI 1 "08 May 2012" | |||
.SH NAME | |||
.B serdi \- Read and write RDF syntax | |||
.SH SYNOPSIS | |||
serdi [OPTION]... INPUT BASE_URI | |||
.SH OPTIONS | |||
.TP | |||
\fB\-b\fR | |||
Fast bulk output for large serialisations. | |||
.TP | |||
\fB\-c PREFIX\fR | |||
Chop PREFIX from matching blank node IDs. | |||
.TP | |||
\fB\-e\fR | |||
Eat input one character at a time, rather than a page at a time which is the | |||
default. This is useful when reading from a pipe since output will be | |||
generated immediately as input arrives, rather than waiting until an entire | |||
page of input has arrived. With this option serdi uses one page less memory, | |||
but will likely be significantly slower. | |||
.TP | |||
\fB\-f\fR | |||
Keep full URIs in input (don't qualify). | |||
.TP | |||
\fB\-h\fR | |||
Print the command line options. | |||
.TP | |||
\fB\-i SYNTAX\fR | |||
Read input in SYNTAX (`turtle' or `ntriples'). | |||
.TP | |||
\fB\-o SYNTAX\fR | |||
Write output in SYNTAX (`turtle' or `ntriples'). | |||
.TP | |||
\fB\-p PREFIX\fR | |||
Add PREFIX to blank node IDs. | |||
.TP | |||
\fB\-r ROOT_URI\fR | |||
Keep relative URIs within ROOT_URI. | |||
.TP | |||
\fB\-s INPUT\fR | |||
Parse INPUT as a string (terminates options). | |||
.TP | |||
\fB\-v\fR | |||
Display version information and exit. | |||
.SH AUTHOR | |||
Serdi was written by David Robillard <d@drobilla.net> | |||
.SH COPYRIGHT | |||
Copyright \(co 2011-2012 David Robillard. | |||
.br | |||
License: <http://www.opensource.org/licenses/isc> | |||
.br | |||
This is free software; you are free to change and redistribute it. | |||
.br | |||
There is NO WARRANTY, to the extent permitted by law. | |||
.SH "SEE ALSO" | |||
<http://drobilla.net/software/serd> |
@@ -0,0 +1,563 @@ | |||
body { | |||
font-size: medium; | |||
font-family: sans-serif; | |||
} | |||
#top { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
padding: 0; | |||
border-bottom: 1px solid #DDD; | |||
margin-bottom: 1ex; | |||
font-size: xx-large; | |||
font-weight: bold; | |||
} | |||
div.header { | |||
display: none; | |||
} | |||
.tabs { | |||
display: none; | |||
} | |||
h1 h2 h3 h4 h5 h6 { | |||
font-weight: bold; | |||
} | |||
h1 { | |||
font-size: 164%; | |||
} | |||
h2 { | |||
font-size: 132%; | |||
} | |||
h3 { | |||
font-size: 124%; | |||
} | |||
h4 { | |||
font-size: 116%; | |||
} | |||
h5 { | |||
font-size: 108%; | |||
} | |||
h6 { | |||
font-size: 100%; | |||
} | |||
p { | |||
margin: 0 0 1ex 0; | |||
} | |||
br { | |||
display: none; | |||
} | |||
dt { | |||
font-weight: 700; | |||
} | |||
div.multicol { | |||
} | |||
p.startli,p.startdd,p.starttd { | |||
margin-top: 2px; | |||
} | |||
p.endli { | |||
margin-bottom: 0; | |||
} | |||
p.enddd { | |||
margin-bottom: 4px; | |||
} | |||
p.endtd { | |||
margin-bottom: 2px; | |||
} | |||
caption { | |||
font-weight: 700; | |||
} | |||
span.legend { | |||
font-size: 70%; | |||
text-align: center; | |||
} | |||
h3.version { | |||
font-size: 90%; | |||
text-align: center; | |||
} | |||
div.qindex,div.navtab { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
padding: 2px; | |||
} | |||
div.qindex,div.navpath { | |||
width: 100%; | |||
line-height: 140%; | |||
} | |||
div.navtab { | |||
margin-right: 15px; | |||
} | |||
/* @group Link Styling */ | |||
a { | |||
color: #3D8C57; | |||
text-decoration: none; | |||
} | |||
.contents a:visited { | |||
color: #50755E; | |||
} | |||
a:hover { | |||
text-decoration: underline; | |||
} | |||
a.qindexHL { | |||
background-color: #9CAFD4; | |||
color: #FFF; | |||
border: 1px double #869DCA; | |||
} | |||
a.code { | |||
color: #4665A2; | |||
} | |||
a.codeRef { | |||
color: #4665A2; | |||
} | |||
/* @end */ | |||
dl.el { | |||
margin-left: -1cm; | |||
} | |||
.fragment { | |||
font-family: monospace, fixed; | |||
font-size: 105%; | |||
} | |||
pre.fragment { | |||
border: 1px solid #C4C4C4; | |||
background-color: #F9F9F9; | |||
padding: 4px 6px; | |||
margin: 4px 8px 4px 2px; | |||
overflow: auto; | |||
font-size: 9pt; | |||
line-height: 125%; | |||
} | |||
div.ah { | |||
background-color: #000; | |||
font-weight: 700; | |||
color: #FFF; | |||
margin-bottom: 3px; | |||
margin-top: 3px; | |||
padding: .2em; | |||
border: thin solid #333; | |||
} | |||
div.groupHeader { | |||
margin-left: 16px; | |||
margin-top: 12px; | |||
margin-bottom: 6px; | |||
font-weight: 700; | |||
} | |||
div.groupText { | |||
margin-left: 16px; | |||
font-style: italic; | |||
} | |||
body { | |||
background: #FFF; | |||
color: #000; | |||
margin: 0; | |||
} | |||
div.contents { | |||
margin-top: 10px; | |||
margin-left: 10px; | |||
margin-right: 10px; | |||
} | |||
td.indexkey { | |||
background-color: #EBEFF6; | |||
font-weight: 700; | |||
border: 1px solid #C4CFE5; | |||
margin: 2px 0; | |||
padding: 2px 10px; | |||
} | |||
td.indexvalue { | |||
background-color: #EBEFF6; | |||
border: 1px solid #C4CFE5; | |||
padding: 2px 10px; | |||
margin: 2px 0; | |||
} | |||
tr.memlist { | |||
background-color: #EEF1F7; | |||
} | |||
p.formulaDsp { | |||
text-align: center; | |||
} | |||
img.formulaDsp { | |||
} | |||
img.formulaInl { | |||
vertical-align: middle; | |||
} | |||
div.center { | |||
text-align: center; | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
padding: 0; | |||
} | |||
div.center img { | |||
border: 0; | |||
} | |||
address.footer { | |||
text-align: right; | |||
padding: 0 0.25em 0.25em 0; | |||
} | |||
img.footer { | |||
border: 0; | |||
vertical-align: middle; | |||
} | |||
/* @group Code Colorization */ | |||
span.keyword { | |||
color: green; | |||
} | |||
span.keywordtype { | |||
color: #604020; | |||
} | |||
span.keywordflow { | |||
color: #e08000; | |||
} | |||
span.comment { | |||
color: maroon; | |||
} | |||
span.preprocessor { | |||
color: #806020; | |||
} | |||
span.stringliteral { | |||
color: #002080; | |||
} | |||
span.charliteral { | |||
color: teal; | |||
} | |||
span.vhdldigit { | |||
color: #F0F; | |||
} | |||
span.vhdlkeyword { | |||
color: #700070; | |||
} | |||
span.vhdllogic { | |||
color: red; | |||
} | |||
/* @end */ | |||
td.tiny { | |||
font-size: 75%; | |||
} | |||
.dirtab { | |||
padding: 4px; | |||
border-collapse: collapse; | |||
border: 1px solid #A3B4D7; | |||
} | |||
th.dirtab { | |||
background: #EBEFF6; | |||
font-weight: 700; | |||
} | |||
hr { | |||
height: 0; | |||
border: none; | |||
border-top: 1px solid #DDD; | |||
margin: 2em 0 1em; | |||
} | |||
hr.footer { | |||
height: 1px; | |||
} | |||
/* @group Member Descriptions */ | |||
table.memberdecls { | |||
border-spacing: 0; | |||
font-size: small; | |||
} | |||
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
background-color: #FBFBFB; | |||
margin: 0; | |||
padding: 0.25ex; | |||
} | |||
.mdescLeft,.mdescRight { | |||
color: #555; | |||
} | |||
.memItemLeft,.memItemRight,.memTemplParams { | |||
border-top: 1px solid #DDD; | |||
} | |||
.memItemLeft,.memTemplItemLeft { | |||
white-space: nowrap; | |||
padding-left: 2em; | |||
} | |||
.memTemplParams { | |||
color: #464646; | |||
white-space: nowrap; | |||
} | |||
/* @end */ | |||
/* @group Member Details */ | |||
/* Styles for detailed member documentation */ | |||
.memtemplate { | |||
font-size: 80%; | |||
color: #4665A2; | |||
font-weight: bold; | |||
} | |||
.memnav { | |||
background-color: #EBEFF6; | |||
border: 1px solid #A3B4D7; | |||
text-align: center; | |||
margin: 2px; | |||
margin-right: 15px; | |||
padding: 2px; | |||
} | |||
.memitem { | |||
padding: 0; | |||
margin: 1ex 0 2ex 0; | |||
border: 1px solid #CCC; | |||
} | |||
.memname { | |||
white-space: nowrap; | |||
font-weight: bold; | |||
} | |||
.memproto { | |||
border-bottom: 1px solid #DDD; | |||
padding: 0.5ex; | |||
font-weight: bold; | |||
background-color: #F3F3F3; | |||
} | |||
.memdoc { | |||
padding: 1ex; | |||
background-color: #FBFBFB; | |||
border-top-width: 0; | |||
} | |||
.paramkey { | |||
text-align: right; | |||
} | |||
.paramtype { | |||
white-space: nowrap; | |||
} | |||
.paramname { | |||
color: #602020; | |||
white-space: nowrap; | |||
} | |||
.paramname em { | |||
font-style: normal; | |||
} | |||
/* @end */ | |||
/* @group Directory (tree) */ | |||
/* for the tree view */ | |||
.ftvtree { | |||
font-family: sans-serif; | |||
margin: 0; | |||
} | |||
/* these are for tree view when used as main index */ | |||
.directory { | |||
font-size: 9pt; | |||
font-weight: bold; | |||
margin: 5px; | |||
} | |||
.directory h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory > h3 { | |||
margin-top: 0; | |||
} | |||
.directory p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory img { | |||
vertical-align: -30%; | |||
} | |||
/* these are for tree view when not used as main index */ | |||
.directory-alt { | |||
font-size: 100%; | |||
font-weight: bold; | |||
} | |||
.directory-alt h3 { | |||
margin: 0; | |||
margin-top: 1em; | |||
font-size: 11pt; | |||
} | |||
.directory-alt > h3 { | |||
margin-top: 0; | |||
} | |||
.directory-alt p { | |||
margin: 0; | |||
white-space: nowrap; | |||
} | |||
.directory-alt div { | |||
display: none; | |||
margin: 0; | |||
} | |||
.directory-alt img { | |||
vertical-align: -30%; | |||
} | |||
/* @end */ | |||
div.dynheader { | |||
margin-top: 8px; | |||
} | |||
address { | |||
font-style: normal; | |||
color: #2A3D61; | |||
} | |||
table.doxtable { | |||
border-collapse: collapse; | |||
margin: 0.5ex; | |||
} | |||
table.doxtable td,table.doxtable th { | |||
border: 1px solid #DDD; | |||
padding: 3px 7px 2px; | |||
} | |||
table.doxtable th { | |||
background-color: #F3F3F3; | |||
color: #000; | |||
padding-bottom: 4px; | |||
padding-top: 5px; | |||
text-align: left; | |||
font-weight: bold; | |||
} | |||
.tabsearch { | |||
top: 0; | |||
left: 10px; | |||
height: 36px; | |||
z-index: 101; | |||
overflow: hidden; | |||
font-size: 13px; | |||
} | |||
.navpath ul { | |||
font-size: 11px; | |||
height: 30px; | |||
line-height: 30px; | |||
color: #8AA0CC; | |||
border: 1px solid #C2CDE4; | |||
overflow: hidden; | |||
margin: 0; | |||
padding: 0; | |||
} | |||
.navpath li { | |||
list-style-type: none; | |||
float: left; | |||
padding-left: 10px; | |||
padding-right: 15px; | |||
color: #364D7C; | |||
} | |||
.navpath a { | |||
height: 32px; | |||
display: block; | |||
text-decoration: none; | |||
outline: none; | |||
} | |||
.navpath a:hover { | |||
color: #6884BD; | |||
} | |||
div.summary { | |||
float: right; | |||
font-size: 8pt; | |||
padding-right: 5px; | |||
width: 50%; | |||
text-align: right; | |||
} | |||
div.summary a { | |||
white-space: nowrap; | |||
} | |||
div.header { | |||
background-color: #F3F3F3; | |||
margin: 0; | |||
border-bottom: 1px solid #DDD; | |||
} | |||
div.headertitle { | |||
padding: 5px 5px 5px 10px; | |||
font-size: 180%; | |||
font-weight: bold; | |||
} |
@@ -0,0 +1,10 @@ | |||
prefix=@PREFIX@ | |||
exec_prefix=@EXEC_PREFIX@ | |||
libdir=@LIBDIR@ | |||
includedir=@INCLUDEDIR@ | |||
Name: Serd | |||
Version: @SERD_VERSION@ | |||
Description: Lightweight RDF syntax library | |||
Libs: -L${libdir} -l@LIB_SERD@ | |||
Cflags: -I${includedir}/serd-@SERD_MAJOR_VERSION@ |
@@ -0,0 +1,963 @@ | |||
/* | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
/** | |||
@file serd.h API for Serd, a lightweight RDF syntax library. | |||
*/ | |||
#ifndef SERD_SERD_H | |||
#define SERD_SERD_H | |||
#include <stdarg.h> | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <stdio.h> | |||
#ifdef SERD_SHARED | |||
# ifdef _WIN32 | |||
# define SERD_LIB_IMPORT __declspec(dllimport) | |||
# define SERD_LIB_EXPORT __declspec(dllexport) | |||
# else | |||
# define SERD_LIB_IMPORT __attribute__((visibility("default"))) | |||
# define SERD_LIB_EXPORT __attribute__((visibility("default"))) | |||
# endif | |||
# ifdef SERD_INTERNAL | |||
# define SERD_API SERD_LIB_EXPORT | |||
# else | |||
# define SERD_API SERD_LIB_IMPORT | |||
# endif | |||
#else | |||
# define SERD_API | |||
#endif | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#else | |||
# include <stdbool.h> | |||
#endif | |||
/** | |||
@defgroup serd Serd | |||
A lightweight RDF syntax library. | |||
@{ | |||
*/ | |||
/** | |||
Environment. | |||
Represents the state required to resolve a CURIE or relative URI, e.g. the | |||
base URI and set of namespace prefixes at a particular point. | |||
*/ | |||
typedef struct SerdEnvImpl SerdEnv; | |||
/** | |||
RDF reader. | |||
Parses RDF by calling user-provided sink functions as input is consumed | |||
(much like an XML SAX parser). | |||
*/ | |||
typedef struct SerdReaderImpl SerdReader; | |||
/** | |||
RDF writer. | |||
Provides a number of functions to allow writing RDF syntax out to some | |||
stream. These functions are deliberately compatible with the sink functions | |||
used by SerdReader, so a reader can be directly connected to a writer to | |||
re-serialise a document with minimal overhead. | |||
*/ | |||
typedef struct SerdWriterImpl SerdWriter; | |||
/** | |||
Return status code. | |||
*/ | |||
typedef enum { | |||
SERD_SUCCESS, /**< No error */ | |||
SERD_FAILURE, /**< Non-fatal failure */ | |||
SERD_ERR_UNKNOWN, /**< Unknown error */ | |||
SERD_ERR_BAD_SYNTAX, /**< Invalid syntax */ | |||
SERD_ERR_BAD_ARG, /**< Invalid argument */ | |||
SERD_ERR_NOT_FOUND, /**< Not found */ | |||
SERD_ERR_ID_CLASH, /**< Encountered clashing blank node IDs */ | |||
SERD_ERR_BAD_CURIE, /**< Invalid CURIE (e.g. prefix does not exist) */ | |||
SERD_ERR_INTERNAL /**< Unexpected internal error (should not happen) */ | |||
} SerdStatus; | |||
/** | |||
RDF syntax type. | |||
*/ | |||
typedef enum { | |||
/** | |||
Turtle - Terse RDF Triple Language (UTF-8). | |||
@see <a href="http://www.w3.org/TeamSubmission/turtle/">Turtle</a> | |||
*/ | |||
SERD_TURTLE = 1, | |||
/** | |||
NTriples - Line-based RDF triples (ASCII). | |||
@see <a href="http://www.w3.org/TR/rdf-testcases#ntriples">NTriples</a> | |||
*/ | |||
SERD_NTRIPLES = 2 | |||
} SerdSyntax; | |||
/** | |||
Flags indication inline abbreviation information for a statement. | |||
*/ | |||
typedef enum { | |||
SERD_EMPTY_S = 1 << 1, /**< Empty blank node subject */ | |||
SERD_EMPTY_O = 1 << 2, /**< Empty blank node object */ | |||
SERD_ANON_S_BEGIN = 1 << 3, /**< Start of anonymous subject */ | |||
SERD_ANON_O_BEGIN = 1 << 4, /**< Start of anonymous object */ | |||
SERD_ANON_CONT = 1 << 5, /**< Continuation of anonymous node */ | |||
SERD_LIST_S_BEGIN = 1 << 6, /**< Start of list subject */ | |||
SERD_LIST_O_BEGIN = 1 << 7, /**< Start of list object */ | |||
SERD_LIST_CONT = 1 << 8 /**< Continuation of list */ | |||
} SerdStatementFlag; | |||
/** | |||
Bitwise OR of SerdNodeFlag values. | |||
*/ | |||
typedef uint32_t SerdStatementFlags; | |||
/** | |||
Type of a syntactic RDF node. | |||
This is more precise than the type of an abstract RDF node. An abstract | |||
node is either a resource, literal, or blank. In syntax there are two ways | |||
to refer to a resource (by URI or CURIE) and two ways to refer to a blank | |||
(by ID or anonymously). Anonymous (inline) blank nodes are expressed using | |||
SerdStatementFlags rather than this type. | |||
*/ | |||
typedef enum { | |||
/** | |||
The type of a nonexistent node. | |||
This type is useful as a sentinel, but is never emitted by the reader. | |||
*/ | |||
SERD_NOTHING = 0, | |||
/** | |||
Literal value. | |||
A literal optionally has either a language, or a datatype (not both). | |||
*/ | |||
SERD_LITERAL = 1, | |||
/** | |||
URI (absolute or relative). | |||
Value is an unquoted URI string, which is either a relative reference | |||
with respect to the current base URI (e.g. "foo/bar"), or an absolute | |||
URI (e.g. "http://example.org/foo"). | |||
@see <a href="http://tools.ietf.org/html/rfc3986">RFC3986</a>. | |||
*/ | |||
SERD_URI = 2, | |||
/** | |||
CURIE, a shortened URI. | |||
Value is an unquoted CURIE string relative to the current environment, | |||
e.g. "rdf:type". | |||
@see <a href="http://www.w3.org/TR/curie">CURIE Syntax 1.0</a> | |||
*/ | |||
SERD_CURIE = 3, | |||
/** | |||
A blank node. | |||
Value is a blank node ID, e.g. "id3", which is meaningful only within | |||
this serialisation. | |||
@see <a href="http://www.w3.org/TeamSubmission/turtle#nodeID">Turtle | |||
<tt>nodeID</tt></a> | |||
*/ | |||
SERD_BLANK = 4, | |||
} SerdType; | |||
/** | |||
Flags indicating certain string properties relevant to serialisation. | |||
*/ | |||
typedef enum { | |||
SERD_HAS_NEWLINE = 1, /**< Contains line breaks ('\\n' or '\\r') */ | |||
SERD_HAS_QUOTE = 1 << 1 /**< Contains quotes ('"') */ | |||
} SerdNodeFlag; | |||
/** | |||
Bitwise OR of SerdNodeFlag values. | |||
*/ | |||
typedef uint32_t SerdNodeFlags; | |||
/** | |||
A syntactic RDF node. | |||
*/ | |||
typedef struct { | |||
const uint8_t* buf; /**< Value string */ | |||
size_t n_bytes; /**< Size in bytes (not including null) */ | |||
size_t n_chars; /**< Length in characters (not including null)*/ | |||
SerdNodeFlags flags; /**< Node flags (e.g. string properties) */ | |||
SerdType type; /**< Node type */ | |||
} SerdNode; | |||
/** | |||
An unterminated string fragment. | |||
*/ | |||
typedef struct { | |||
const uint8_t* buf; /**< Start of chunk */ | |||
size_t len; /**< Length of chunk in bytes */ | |||
} SerdChunk; | |||
/** | |||
An error description. | |||
*/ | |||
typedef struct { | |||
SerdStatus status; /**< Error code */ | |||
const uint8_t* filename; /**< File where error was encountered, or NULL */ | |||
unsigned line; /**< Line where error was encountered, or 0 */ | |||
unsigned col; /**< Column where error was encountered */ | |||
const char* fmt; /**< Message format string (printf style) */ | |||
va_list* args; /**< Arguments for fmt */ | |||
} SerdError; | |||
/** | |||
A parsed URI. | |||
This struct directly refers to chunks in other strings, it does not own any | |||
memory itself. Thus, URIs can be parsed and/or resolved against a base URI | |||
in-place without allocating memory. | |||
*/ | |||
typedef struct { | |||
SerdChunk scheme; /**< Scheme */ | |||
SerdChunk authority; /**< Authority */ | |||
SerdChunk path_base; /**< Path prefix if relative */ | |||
SerdChunk path; /**< Path suffix */ | |||
SerdChunk query; /**< Query */ | |||
SerdChunk fragment; /**< Fragment */ | |||
} SerdURI; | |||
/** | |||
Syntax style options. | |||
The style of the writer output can be controlled by ORing together | |||
values from this enumeration. Note that some options are only supported | |||
for some syntaxes (e.g. NTriples does not support abbreviation and is | |||
always ASCII). | |||
*/ | |||
typedef enum { | |||
SERD_STYLE_ABBREVIATED = 1, /**< Abbreviate triples when possible. */ | |||
SERD_STYLE_ASCII = 1 << 1, /**< Escape all non-ASCII characters. */ | |||
SERD_STYLE_RESOLVED = 1 << 2, /**< Resolve URIs against base URI. */ | |||
SERD_STYLE_CURIED = 1 << 3, /**< Shorten URIs into CURIEs. */ | |||
SERD_STYLE_BULK = 1 << 4 /**< Write output in pages. */ | |||
} SerdStyle; | |||
/** | |||
@name String Utilities | |||
@{ | |||
*/ | |||
/** | |||
Return a string describing a status code. | |||
*/ | |||
SERD_API | |||
const uint8_t* | |||
serd_strerror(SerdStatus status); | |||
/** | |||
Measure a UTF-8 string. | |||
@return Length of @c str in characters (except NULL). | |||
@param str A null-terminated UTF-8 string. | |||
@param n_bytes (Output) Set to the size of @c str in bytes (except NULL). | |||
@param flags (Output) Set to the applicable flags. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags); | |||
/** | |||
Parse a string to a double. | |||
The API of this function is identical to the standard C strtod function, | |||
except this function is locale-independent and always matches the lexical | |||
format used in the Turtle grammar (the decimal point is always "."). | |||
*/ | |||
SERD_API | |||
double | |||
serd_strtod(const char* str, char** endptr); | |||
/** | |||
Decode a base64 string. | |||
This function can be used to deserialise a blob node created with | |||
serd_node_new_blob(). | |||
@param str Base64 string to decode. | |||
@param len The length of @c str. | |||
@param size Set to the size of the returned blob in bytes. | |||
@return A newly allocated blob which must be freed with free(). | |||
*/ | |||
SERD_API | |||
void* | |||
serd_base64_decode(const uint8_t* str, size_t len, size_t* size); | |||
/** | |||
@} | |||
@name URI | |||
@{ | |||
*/ | |||
static const SerdURI SERD_URI_NULL = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; | |||
/** | |||
Return the local path for @c uri, or NULL if @c uri is not a file URI. | |||
Note this (inappropriately named) function only removes the file scheme if | |||
necessary, and returns @c uri unmodified if it is an absolute path. Percent | |||
encoding and other issues are not handled, to properly convert a file URI to | |||
a path, use serd_file_uri_parse(). | |||
*/ | |||
SERD_API | |||
const uint8_t* | |||
serd_uri_to_path(const uint8_t* uri); | |||
/** | |||
Get the unescaped path and hostname from a file URI. | |||
@param uri A file URI. | |||
@param hostname If non-NULL, set to the hostname, if present. | |||
@return The path component of the URI. | |||
Both the returned path and @c hostname (if applicable) are owned by the | |||
caller and must be freed with free(). | |||
*/ | |||
SERD_API | |||
uint8_t* | |||
serd_file_uri_parse(const uint8_t* uri, uint8_t** hostname); | |||
/** | |||
Return true iff @c utf8 starts with a valid URI scheme. | |||
*/ | |||
SERD_API | |||
bool | |||
serd_uri_string_has_scheme(const uint8_t* utf8); | |||
/** | |||
Parse @c utf8, writing result to @c out. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_uri_parse(const uint8_t* utf8, SerdURI* out); | |||
/** | |||
Set @c out to @c uri resolved against @c base. | |||
*/ | |||
SERD_API | |||
void | |||
serd_uri_resolve(const SerdURI* uri, const SerdURI* base, SerdURI* out); | |||
/** | |||
Sink function for raw string output. | |||
*/ | |||
typedef size_t (*SerdSink)(const void* buf, size_t len, void* stream); | |||
/** | |||
Serialise @c uri with a series of calls to @c sink. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_uri_serialise(const SerdURI* uri, SerdSink sink, void* stream); | |||
/** | |||
Serialise @c uri relative to @c base with a series of calls to @c sink. | |||
The @c uri is written as a relative URI iff if it a child of @c base and @c | |||
root. The optional @c root parameter must be a prefix of @c base and can be | |||
used keep up-references ("../") within a certain namespace. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_uri_serialise_relative(const SerdURI* uri, | |||
const SerdURI* base, | |||
const SerdURI* root, | |||
SerdSink sink, | |||
void* stream); | |||
/** | |||
@} | |||
@name Node | |||
@{ | |||
*/ | |||
static const SerdNode SERD_NODE_NULL = { 0, 0, 0, 0, SERD_NOTHING }; | |||
/** | |||
Make a (shallow) node from @c str. | |||
This measures, but does not copy, @c str. No memory is allocated. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_from_string(SerdType type, const uint8_t* str); | |||
/** | |||
Make a deep copy of @c node. | |||
@return a node that the caller must free with @ref serd_node_free. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_copy(const SerdNode* node); | |||
/** | |||
Return true iff @c a is equal to @c b. | |||
*/ | |||
SERD_API | |||
bool | |||
serd_node_equals(const SerdNode* a, const SerdNode* b); | |||
/** | |||
Simple wrapper for serd_node_new_uri to resolve a URI node. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri_from_node(const SerdNode* uri_node, | |||
const SerdURI* base, | |||
SerdURI* out); | |||
/** | |||
Simple wrapper for serd_node_new_uri to resolve a URI string. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri_from_string(const uint8_t* str, | |||
const SerdURI* base, | |||
SerdURI* out); | |||
/** | |||
Create a new file URI node from a file system path and optional hostname. | |||
Backslashes in Windows paths will be converted and '%' will always be | |||
percent encoded. If @c escape is true, all other invalid characters will be | |||
percent encoded as well. | |||
If @c path is relative, @c hostname is ignored. | |||
If @c out is not NULL, it will be set to the parsed URI. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_file_uri(const uint8_t* path, | |||
const uint8_t* hostname, | |||
SerdURI* out, | |||
bool escape); | |||
/** | |||
Create a new node by serialising @c uri into a new string. | |||
@param uri The URI to parse and serialise. | |||
@param base Base URI to resolve @c uri against (or NULL for no resolution). | |||
@param out Set to the parsing of the new URI (i.e. points only to | |||
memory owned by the new returned node). | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out); | |||
/** | |||
Create a new node by serialising @c d into an xsd:decimal string. | |||
The resulting node will always contain a `.', start with a digit, and end | |||
with a digit (i.e. will have a leading and/or trailing `0' if necessary). | |||
It will never be in scientific notation. A maximum of @c frac_digits digits | |||
will be written after the decimal point, but trailing zeros will | |||
automatically be omitted (except one if @c d is a round integer). | |||
Note that about 16 and 8 fractional digits are required to precisely | |||
represent a double and float, respectively. | |||
@param d The value for the new node. | |||
@param frac_digits The maximum number of digits after the decimal place. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_decimal(double d, unsigned frac_digits); | |||
/** | |||
Create a new node by serialising @c i into an xsd:integer string. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_integer(int64_t i); | |||
/** | |||
Create a node by serialising @c buf into an xsd:base64Binary string. | |||
This function can be used to make a serialisable node out of arbitrary | |||
binary data, which can be decoded using serd_base64_decode(). | |||
@param buf Raw binary input data. | |||
@param size Size of @c buf. | |||
@param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_node_new_blob(const void* buf, size_t size, bool wrap_lines); | |||
/** | |||
Free any data owned by @c node. | |||
Note that if @c node is itself dynamically allocated (which is not the case | |||
for nodes created internally by serd), it will not be freed. | |||
*/ | |||
SERD_API | |||
void | |||
serd_node_free(SerdNode* node); | |||
/** | |||
@} | |||
@name Event Handlers | |||
@{ | |||
*/ | |||
/** | |||
Sink (callback) for errors. | |||
@param handle Handle for user data. | |||
@param error Error description. | |||
*/ | |||
typedef SerdStatus (*SerdErrorSink)(void* handle, | |||
const SerdError* error); | |||
/** | |||
Sink (callback) for base URI changes. | |||
Called whenever the base URI of the serialisation changes. | |||
*/ | |||
typedef SerdStatus (*SerdBaseSink)(void* handle, | |||
const SerdNode* uri); | |||
/** | |||
Sink (callback) for namespace definitions. | |||
Called whenever a prefix is defined in the serialisation. | |||
*/ | |||
typedef SerdStatus (*SerdPrefixSink)(void* handle, | |||
const SerdNode* name, | |||
const SerdNode* uri); | |||
/** | |||
Sink (callback) for statements. | |||
Called for every RDF statement in the serialisation. | |||
*/ | |||
typedef SerdStatus (*SerdStatementSink)(void* handle, | |||
SerdStatementFlags flags, | |||
const SerdNode* graph, | |||
const SerdNode* subject, | |||
const SerdNode* predicate, | |||
const SerdNode* object, | |||
const SerdNode* object_datatype, | |||
const SerdNode* object_lang); | |||
/** | |||
Sink (callback) for anonymous node end markers. | |||
This is called to indicate that the anonymous node with the given | |||
@c value will no longer be referred to by any future statements | |||
(i.e. the anonymous serialisation of the node is finished). | |||
*/ | |||
typedef SerdStatus (*SerdEndSink)(void* handle, | |||
const SerdNode* node); | |||
/** | |||
@} | |||
@name Environment | |||
@{ | |||
*/ | |||
/** | |||
Create a new environment. | |||
*/ | |||
SERD_API | |||
SerdEnv* | |||
serd_env_new(const SerdNode* base_uri); | |||
/** | |||
Free @c ns. | |||
*/ | |||
SERD_API | |||
void | |||
serd_env_free(SerdEnv* env); | |||
/** | |||
Get the current base URI. | |||
*/ | |||
SERD_API | |||
const SerdNode* | |||
serd_env_get_base_uri(const SerdEnv* env, | |||
SerdURI* out); | |||
/** | |||
Set the current base URI. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_base_uri(SerdEnv* env, | |||
const SerdNode* uri); | |||
/** | |||
Set a namespace prefix. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_prefix(SerdEnv* env, | |||
const SerdNode* name, | |||
const SerdNode* uri); | |||
/** | |||
Set a namespace prefix. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_prefix_from_strings(SerdEnv* env, | |||
const uint8_t* name, | |||
const uint8_t* uri); | |||
/** | |||
Qualify @c uri into a CURIE if possible. | |||
*/ | |||
SERD_API | |||
bool | |||
serd_env_qualify(const SerdEnv* env, | |||
const SerdNode* uri, | |||
SerdNode* prefix, | |||
SerdChunk* suffix); | |||
/** | |||
Expand @c curie. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_env_expand(const SerdEnv* env, | |||
const SerdNode* curie, | |||
SerdChunk* uri_prefix, | |||
SerdChunk* uri_suffix); | |||
/** | |||
Expand @c node, which must be a CURIE or URI, to a full URI. | |||
*/ | |||
SERD_API | |||
SerdNode | |||
serd_env_expand_node(const SerdEnv* env, | |||
const SerdNode* node); | |||
/** | |||
Call @c func for each prefix defined in @c env. | |||
*/ | |||
SERD_API | |||
void | |||
serd_env_foreach(const SerdEnv* env, | |||
SerdPrefixSink func, | |||
void* handle); | |||
/** | |||
@} | |||
@name Reader | |||
@{ | |||
*/ | |||
/** | |||
Create a new RDF reader. | |||
*/ | |||
SERD_API | |||
SerdReader* | |||
serd_reader_new(SerdSyntax syntax, | |||
void* handle, | |||
void (*free_handle)(void*), | |||
SerdBaseSink base_sink, | |||
SerdPrefixSink prefix_sink, | |||
SerdStatementSink statement_sink, | |||
SerdEndSink end_sink); | |||
/** | |||
Set a function to be called when errors occur during reading. | |||
The @p error_sink will be called with @p handle as its first argument. If | |||
no error function is set, errors are printed to stderr in GCC style. | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_set_error_sink(SerdReader* reader, | |||
SerdErrorSink error_sink, | |||
void* handle); | |||
/** | |||
Return the @c handle passed to @ref serd_reader_new. | |||
*/ | |||
SERD_API | |||
void* | |||
serd_reader_get_handle(const SerdReader* reader); | |||
/** | |||
Set a prefix to be added to all blank node identifiers. | |||
This is useful when multiple files are to be parsed into the same output | |||
(e.g. a store, or other files). Since Serd preserves blank node IDs, this | |||
could cause conflicts where two non-equivalent blank nodes are merged, | |||
resulting in corrupt data. By setting a unique blank node prefix for each | |||
parsed file, this can be avoided, while preserving blank node names. | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_add_blank_prefix(SerdReader* reader, | |||
const uint8_t* prefix); | |||
/** | |||
Set the URI of the default graph. | |||
If this is set, the reader will emit quads with the graph set to the given | |||
node for any statements that are not in a named graph (which is currently | |||
all of them since Serd currently does not support any graph syntaxes). | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_set_default_graph(SerdReader* reader, | |||
const SerdNode* graph); | |||
/** | |||
Read a file at a given @c uri. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_file(SerdReader* reader, | |||
const uint8_t* uri); | |||
/** | |||
Start an incremental read from a file handle. | |||
Iff @p bulk is true, @p file will be read a page at a time. This is more | |||
efficient, but uses a page of memory and means that an entire page of input | |||
must be ready before any callbacks will fire. To react as soon as input | |||
arrives, set @p bulk to false. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_start_stream(SerdReader* me, | |||
FILE* file, | |||
const uint8_t* name, | |||
bool bulk); | |||
/** | |||
Read a single "chunk" of data during an incremental read. | |||
This function will read a single top level description, and return. This | |||
may be a directive, statement, or several statements; essentially it reads | |||
until a '.' is encountered. This is particularly useful for reading | |||
directly from a pipe or socket. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_chunk(SerdReader* me); | |||
/** | |||
Finish an incremental read from a file handle. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_end_stream(SerdReader* me); | |||
/** | |||
Read @c file. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_file_handle(SerdReader* reader, | |||
FILE* file, | |||
const uint8_t* name); | |||
/** | |||
Read @c utf8. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_reader_read_string(SerdReader* me, const uint8_t* utf8); | |||
/** | |||
Free @c reader. | |||
*/ | |||
SERD_API | |||
void | |||
serd_reader_free(SerdReader* reader); | |||
/** | |||
@} | |||
@name Writer | |||
@{ | |||
*/ | |||
/** | |||
Create a new RDF writer. | |||
*/ | |||
SERD_API | |||
SerdWriter* | |||
serd_writer_new(SerdSyntax syntax, | |||
SerdStyle style, | |||
SerdEnv* env, | |||
const SerdURI* base_uri, | |||
SerdSink sink, | |||
void* stream); | |||
/** | |||
Free @c writer. | |||
*/ | |||
SERD_API | |||
void | |||
serd_writer_free(SerdWriter* writer); | |||
/** | |||
Return the env used by @c writer. | |||
*/ | |||
SERD_API | |||
SerdEnv* | |||
serd_writer_get_env(SerdWriter* writer); | |||
/** | |||
A convenience sink function for writing to a FILE*. | |||
This function can be used as a SerdSink when writing to a FILE*. The | |||
@c stream parameter must be a FILE* opened for writing. | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_file_sink(const void* buf, size_t len, void* stream); | |||
/** | |||
A convenience sink function for writing to a string. | |||
This function can be used as a SerdSink to write to a SerdChunk which is | |||
resized as necessary with realloc(). The @c stream parameter must point to | |||
an initialized SerdChunk. When the write is finished, the string should be | |||
retrieved with serd_chunk_sink_finish(). | |||
*/ | |||
SERD_API | |||
size_t | |||
serd_chunk_sink(const void* buf, size_t len, void* stream); | |||
/** | |||
Finish a serialisation to a chunk with serd_chunk_sink(). | |||
The returned string is the result of the serialisation, which is NULL | |||
terminated (by this function) and owned by the caller. | |||
*/ | |||
SERD_API | |||
uint8_t* | |||
serd_chunk_sink_finish(SerdChunk* stream); | |||
/** | |||
Set a function to be called when errors occur during writing. | |||
The @p error_sink will be called with @p handle as its first argument. If | |||
no error function is set, errors are printed to stderr. | |||
*/ | |||
SERD_API | |||
void | |||
serd_writer_set_error_sink(SerdWriter* writer, | |||
SerdErrorSink error_sink, | |||
void* handle); | |||
/** | |||
Set a prefix to be removed from matching blank node identifiers. | |||
*/ | |||
SERD_API | |||
void | |||
serd_writer_chop_blank_prefix(SerdWriter* writer, | |||
const uint8_t* prefix); | |||
/** | |||
Set the current output base URI (and emit directive if applicable). | |||
Note this function can be safely casted to SerdBaseSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_set_base_uri(SerdWriter* writer, | |||
const SerdNode* uri); | |||
/** | |||
Set the current root URI. | |||
The root URI should be a prefix of the base URI. The path of the root URI | |||
is the highest path any relative up-reference can refer to. For example, | |||
with root <file:///foo/root> and base <file:///foo/root/base>, | |||
<file:///foo/root> will be written as <../>, but <file:///foo> will be | |||
written non-relatively as <file:///foo>. If the root is not explicitly set, | |||
it defaults to the base URI, so no up-references will be created at all. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_set_root_uri(SerdWriter* writer, | |||
const SerdNode* uri); | |||
/** | |||
Set a namespace prefix (and emit directive if applicable). | |||
Note this function can be safely casted to SerdPrefixSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_set_prefix(SerdWriter* writer, | |||
const SerdNode* name, | |||
const SerdNode* uri); | |||
/** | |||
Write a statement. | |||
Note this function can be safely casted to SerdStatementSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_write_statement(SerdWriter* writer, | |||
SerdStatementFlags flags, | |||
const SerdNode* graph, | |||
const SerdNode* subject, | |||
const SerdNode* predicate, | |||
const SerdNode* object, | |||
const SerdNode* object_datatype, | |||
const SerdNode* object_lang); | |||
/** | |||
Mark the end of an anonymous node's description. | |||
Note this function can be safely casted to SerdEndSink. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_end_anon(SerdWriter* writer, | |||
const SerdNode* node); | |||
/** | |||
Finish a write. | |||
*/ | |||
SERD_API | |||
SerdStatus | |||
serd_writer_finish(SerdWriter* writer); | |||
/** | |||
@} | |||
@} | |||
*/ | |||
#ifdef __cplusplus | |||
} /* extern "C" */ | |||
#endif | |||
#endif /* SERD_SERD_H */ |
@@ -0,0 +1,271 @@ | |||
/* | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "serd_internal.h" | |||
#include <assert.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
typedef struct { | |||
SerdNode name; | |||
SerdNode uri; | |||
} SerdPrefix; | |||
struct SerdEnvImpl { | |||
SerdPrefix* prefixes; | |||
size_t n_prefixes; | |||
SerdNode base_uri_node; | |||
SerdURI base_uri; | |||
}; | |||
SERD_API | |||
SerdEnv* | |||
serd_env_new(const SerdNode* base_uri) | |||
{ | |||
SerdEnv* env = (SerdEnv*)malloc(sizeof(struct SerdEnvImpl)); | |||
env->prefixes = NULL; | |||
env->n_prefixes = 0; | |||
env->base_uri_node = SERD_NODE_NULL; | |||
env->base_uri = SERD_URI_NULL; | |||
if (base_uri) { | |||
serd_env_set_base_uri(env, base_uri); | |||
} | |||
return env; | |||
} | |||
SERD_API | |||
void | |||
serd_env_free(SerdEnv* env) | |||
{ | |||
for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
serd_node_free(&env->prefixes[i].name); | |||
serd_node_free(&env->prefixes[i].uri); | |||
} | |||
free(env->prefixes); | |||
serd_node_free(&env->base_uri_node); | |||
free(env); | |||
} | |||
SERD_API | |||
const SerdNode* | |||
serd_env_get_base_uri(const SerdEnv* env, | |||
SerdURI* out) | |||
{ | |||
if (out) { | |||
*out = env->base_uri; | |||
} | |||
return &env->base_uri_node; | |||
} | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_base_uri(SerdEnv* env, | |||
const SerdNode* uri_node) | |||
{ | |||
// Resolve base URI and create a new node and URI for it | |||
SerdURI base_uri; | |||
SerdNode base_uri_node = serd_node_new_uri_from_node( | |||
uri_node, &env->base_uri, &base_uri); | |||
if (base_uri_node.buf) { | |||
// Replace the current base URI | |||
serd_node_free(&env->base_uri_node); | |||
env->base_uri_node = base_uri_node; | |||
env->base_uri = base_uri; | |||
return SERD_SUCCESS; | |||
} | |||
return SERD_ERR_BAD_ARG; | |||
} | |||
static inline SerdPrefix* | |||
serd_env_find(const SerdEnv* env, | |||
const uint8_t* name, | |||
size_t name_len) | |||
{ | |||
for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
const SerdNode* const prefix_name = &env->prefixes[i].name; | |||
if (prefix_name->n_bytes == name_len) { | |||
if (!memcmp(prefix_name->buf, name, name_len)) { | |||
return &env->prefixes[i]; | |||
} | |||
} | |||
} | |||
return NULL; | |||
} | |||
static void | |||
serd_env_add(SerdEnv* env, | |||
const SerdNode* name, | |||
const SerdNode* uri) | |||
{ | |||
SerdPrefix* const prefix = serd_env_find(env, name->buf, name->n_bytes); | |||
if (prefix) { | |||
SerdNode old_prefix_uri = prefix->uri; | |||
prefix->uri = serd_node_copy(uri); | |||
serd_node_free(&old_prefix_uri); | |||
} else { | |||
env->prefixes = (SerdPrefix*)realloc( | |||
env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix)); | |||
env->prefixes[env->n_prefixes - 1].name = serd_node_copy(name); | |||
env->prefixes[env->n_prefixes - 1].uri = serd_node_copy(uri); | |||
} | |||
} | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_prefix(SerdEnv* env, | |||
const SerdNode* name, | |||
const SerdNode* uri_node) | |||
{ | |||
if (!name->buf || uri_node->type != SERD_URI) { | |||
return SERD_ERR_BAD_ARG; | |||
} else if (serd_uri_string_has_scheme(uri_node->buf)) { | |||
// Set prefix to absolute URI | |||
serd_env_add(env, name, uri_node); | |||
} else { | |||
// Resolve relative URI and create a new node and URI for it | |||
SerdURI abs_uri; | |||
SerdNode abs_uri_node = serd_node_new_uri_from_node( | |||
uri_node, &env->base_uri, &abs_uri); | |||
// Set prefix to resolved (absolute) URI | |||
serd_env_add(env, name, &abs_uri_node); | |||
serd_node_free(&abs_uri_node); | |||
} | |||
return SERD_SUCCESS; | |||
} | |||
SERD_API | |||
SerdStatus | |||
serd_env_set_prefix_from_strings(SerdEnv* env, | |||
const uint8_t* name, | |||
const uint8_t* uri) | |||
{ | |||
const SerdNode name_node = serd_node_from_string(SERD_LITERAL, name); | |||
const SerdNode uri_node = serd_node_from_string(SERD_URI, uri); | |||
return serd_env_set_prefix(env, &name_node, &uri_node); | |||
} | |||
static inline bool | |||
is_nameChar(const uint8_t c) | |||
{ | |||
return is_alpha(c) || is_digit(c) || c == '_'; | |||
} | |||
/** | |||
Return true iff @c buf is a valid prefixed name suffix. | |||
TODO: This is more strict than it should be. | |||
*/ | |||
static inline bool | |||
is_name(const uint8_t* buf, size_t len) | |||
{ | |||
for (size_t i = 0; i < len; ++i) { | |||
if (!is_nameChar(buf[i])) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
SERD_API | |||
bool | |||
serd_env_qualify(const SerdEnv* env, | |||
const SerdNode* uri, | |||
SerdNode* prefix_name, | |||
SerdChunk* suffix) | |||
{ | |||
for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
const SerdNode* const prefix_uri = &env->prefixes[i].uri; | |||
if (uri->n_bytes >= prefix_uri->n_bytes) { | |||
if (!strncmp((const char*)uri->buf, | |||
(const char*)prefix_uri->buf, | |||
prefix_uri->n_bytes)) { | |||
*prefix_name = env->prefixes[i].name; | |||
suffix->buf = uri->buf + prefix_uri->n_bytes; | |||
suffix->len = uri->n_bytes - prefix_uri->n_bytes; | |||
if (is_name(suffix->buf, suffix->len)) { | |||
return true; | |||
} | |||
} | |||
} | |||
} | |||
return false; | |||
} | |||
SERD_API | |||
SerdStatus | |||
serd_env_expand(const SerdEnv* env, | |||
const SerdNode* qname, | |||
SerdChunk* uri_prefix, | |||
SerdChunk* uri_suffix) | |||
{ | |||
const uint8_t* const colon = (const uint8_t*)memchr( | |||
qname->buf, ':', qname->n_bytes + 1); | |||
if (!colon) { | |||
return SERD_ERR_BAD_ARG; // Invalid qname | |||
} | |||
const size_t name_len = colon - qname->buf; | |||
const SerdPrefix* const prefix = serd_env_find(env, qname->buf, name_len); | |||
if (prefix) { | |||
uri_prefix->buf = prefix->uri.buf; | |||
uri_prefix->len = prefix->uri.n_bytes; | |||
uri_suffix->buf = colon + 1; | |||
uri_suffix->len = qname->n_bytes - (colon - qname->buf) - 1; | |||
return SERD_SUCCESS; | |||
} | |||
return SERD_ERR_NOT_FOUND; | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_env_expand_node(const SerdEnv* env, | |||
const SerdNode* node) | |||
{ | |||
switch (node->type) { | |||
case SERD_CURIE: { | |||
SerdChunk prefix; | |||
SerdChunk suffix; | |||
if (serd_env_expand(env, node, &prefix, &suffix)) { | |||
return SERD_NODE_NULL; | |||
} | |||
const size_t len = prefix.len + suffix.len; // FIXME: UTF-8? | |||
uint8_t* buf = (uint8_t*)malloc(len + 1); | |||
SerdNode ret = { buf, len, len, 0, SERD_URI }; | |||
snprintf((char*)buf, ret.n_bytes + 1, "%s%s", prefix.buf, suffix.buf); | |||
return ret; | |||
} | |||
case SERD_URI: { | |||
SerdURI ignored; | |||
return serd_node_new_uri_from_node(node, &env->base_uri, &ignored); | |||
} | |||
default: | |||
return SERD_NODE_NULL; | |||
} | |||
} | |||
SERD_API | |||
void | |||
serd_env_foreach(const SerdEnv* env, | |||
SerdPrefixSink func, | |||
void* handle) | |||
{ | |||
for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
func(handle, &env->prefixes[i].name, &env->prefixes[i].uri); | |||
} | |||
} |
@@ -0,0 +1,347 @@ | |||
/* | |||
Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
Permission to use, copy, modify, and/or distribute this software for any | |||
purpose with or without fee is hereby granted, provided that the above | |||
copyright notice and this permission notice appear in all copies. | |||
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#include "serd_internal.h" | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#include <float.h> | |||
#ifdef _WIN32 | |||
# define isnan(x) _isnan(x) | |||
# define isinf(x) (!_finite(x)) | |||
#endif | |||
SERD_API | |||
SerdNode | |||
serd_node_from_string(SerdType type, const uint8_t* buf) | |||
{ | |||
if (!buf) { | |||
return SERD_NODE_NULL; | |||
} | |||
uint32_t flags = 0; | |||
size_t buf_n_bytes = 0; | |||
const size_t buf_n_chars = serd_strlen(buf, &buf_n_bytes, &flags); | |||
SerdNode ret = { buf, buf_n_bytes, buf_n_chars, flags, type }; | |||
return ret; | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_node_copy(const SerdNode* node) | |||
{ | |||
if (!node || !node->buf) { | |||
return SERD_NODE_NULL; | |||
} | |||
SerdNode copy = *node; | |||
uint8_t* buf = (uint8_t*)malloc(copy.n_bytes + 1); | |||
memcpy(buf, node->buf, copy.n_bytes + 1); | |||
copy.buf = buf; | |||
return copy; | |||
} | |||
SERD_API | |||
bool | |||
serd_node_equals(const SerdNode* a, const SerdNode* b) | |||
{ | |||
return (a == b) | |||
|| (a->type == b->type | |||
&& a->n_bytes == b->n_bytes | |||
&& a->n_chars == b->n_chars | |||
&& ((a->buf == b->buf) || !memcmp((const char*)a->buf, | |||
(const char*)b->buf, | |||
a->n_bytes + 1))); | |||
} | |||
static size_t | |||
serd_uri_string_length(const SerdURI* uri) | |||
{ | |||
size_t len = uri->path_base.len; | |||
#define ADD_LEN(field, n_delims) \ | |||
if ((field).len) { len += (field).len + (n_delims); } | |||
ADD_LEN(uri->path, 1); // + possible leading `/' | |||
ADD_LEN(uri->scheme, 1); // + trailing `:' | |||
ADD_LEN(uri->authority, 2); // + leading `//' | |||
ADD_LEN(uri->query, 1); // + leading `?' | |||
ADD_LEN(uri->fragment, 1); // + leading `#' | |||
return len + 2; // + 2 for authority `//' | |||
} | |||
static size_t | |||
string_sink(const void* buf, size_t len, void* stream) | |||
{ | |||
uint8_t** ptr = (uint8_t**)stream; | |||
memcpy(*ptr, buf, len); | |||
*ptr += len; | |||
return len; | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri_from_node(const SerdNode* uri_node, | |||
const SerdURI* base, | |||
SerdURI* out) | |||
{ | |||
return (uri_node->type == SERD_URI) | |||
? serd_node_new_uri_from_string(uri_node->buf, base, out) | |||
: SERD_NODE_NULL; | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri_from_string(const uint8_t* str, | |||
const SerdURI* base, | |||
SerdURI* out) | |||
{ | |||
if (!str || str[0] == '\0') { | |||
return serd_node_new_uri(base, NULL, out); // Empty URI => Base URI | |||
} | |||
SerdURI uri; | |||
serd_uri_parse(str, &uri); | |||
return serd_node_new_uri(&uri, base, out); // Resolve/Serialise | |||
} | |||
static inline bool | |||
is_uri_path_char(const uint8_t c) | |||
{ | |||
if (is_alpha(c) || is_digit(c)) { | |||
return true; | |||
} | |||
switch (c) { | |||
case '-': case '.': case '_': case '~': // unreserved | |||
case ':': case '@': // pchar | |||
case '/': // separator | |||
// sub-delims | |||
case '!': case '$': case '&': case '\'': case '(': case ')': | |||
case '*': case '+': case ',': case ';': case '=': | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_node_new_file_uri(const uint8_t* path, | |||
const uint8_t* hostname, | |||
SerdURI* out, | |||
bool escape) | |||
{ | |||
const size_t path_len = strlen((const char*)path); | |||
const size_t hostname_len = hostname ? strlen((const char*)hostname) : 0; | |||
const bool evil = is_windows_path(path); | |||
size_t uri_len = 0; | |||
uint8_t* uri = NULL; | |||
if (path[0] == '/' || is_windows_path(path)) { | |||
uri_len = strlen("file://") + hostname_len + evil; | |||
uri = (uint8_t*)malloc(uri_len + 1); | |||
snprintf((char*)uri, uri_len + 1, "file://%s%s", | |||
hostname ? (const char*)hostname : "", | |||
evil ? "/" : ""); | |||
} | |||
SerdChunk chunk = { uri, uri_len }; | |||
for (size_t i = 0; i < path_len; ++i) { | |||
if (evil && path[i] == '\\') { | |||
serd_chunk_sink("/", 1, &chunk); | |||
} else if (path[i] == '%') { | |||
serd_chunk_sink("%%", 2, &chunk); | |||
} else if (!escape || is_uri_path_char(path[i])) { | |||
serd_chunk_sink(path + i, 1, &chunk); | |||
} else { | |||
char escape_str[4] = { '%', 0, 0, 0 }; | |||
snprintf(escape_str + 1, sizeof(escape_str) - 1, "%X", path[i]); | |||
serd_chunk_sink(escape_str, 3, &chunk); | |||
} | |||
} | |||
serd_chunk_sink_finish(&chunk); | |||
if (out) { | |||
serd_uri_parse(chunk.buf, out); | |||
} | |||
return serd_node_from_string(SERD_URI, chunk.buf); | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out) | |||
{ | |||
SerdURI abs_uri = *uri; | |||
if (base) { | |||
serd_uri_resolve(uri, base, &abs_uri); | |||
} | |||
const size_t len = serd_uri_string_length(&abs_uri); | |||
uint8_t* buf = (uint8_t*)malloc(len + 1); | |||
SerdNode node = { buf, len, len, 0, SERD_URI }; // FIXME: UTF-8 | |||
uint8_t* ptr = buf; | |||
const size_t actual_len = serd_uri_serialise(&abs_uri, string_sink, &ptr); | |||
buf[actual_len] = '\0'; | |||
node.n_bytes = actual_len; | |||
node.n_chars = actual_len; | |||
if (out) { | |||
serd_uri_parse(buf, out); // TODO: cleverly avoid double parse | |||
} | |||
return node; | |||
} | |||
SERD_API | |||
SerdNode | |||
serd_node_new_decimal(double d, unsigned frac_digits) | |||
{ | |||
if (isnan(d) || isinf(d)) { | |||
return SERD_NODE_NULL; | |||
} | |||
const double abs_d = fabs(d); | |||
const unsigned int_digits = (unsigned)fmax(1.0, ceil(log10(abs_d + 1))); | |||
char* buf = (char*)calloc(int_digits + frac_digits + 3, 1); | |||