| @@ -104,3 +104,6 @@ c++/carla-native/zynaddsubfx/UI/SUBnoteUI.cc | |||
| c++/carla-native/zynaddsubfx/UI/SUBnoteUI.h | |||
| c++/carla-native/zynaddsubfx/UI/VirKeyboard.cc | |||
| c++/carla-native/zynaddsubfx/UI/VirKeyboard.h | |||
| # Other | |||
| libs/jackbridge/jack/ | |||
| @@ -7,5 +7,5 @@ else | |||
| fi | |||
| INSTALL_PREFIX="X-PREFIX-X" | |||
| export PATH="$INSTALL_PREFIX"/lib/cadence:$PATH | |||
| exec $PYTHON $INSTALL_PREFIX/share/cadence/src/carla.py --with-libprefix="$INSTALL_PREFIX" "$@" | |||
| export PATH="$INSTALL_PREFIX"/lib/carla:$PATH | |||
| exec $PYTHON $INSTALL_PREFIX/share/carla/carla.py --with-libprefix="$INSTALL_PREFIX" "$@" | |||
| @@ -7,4 +7,4 @@ else | |||
| fi | |||
| INSTALL_PREFIX="X-PREFIX-X" | |||
| exec $PYTHON $INSTALL_PREFIX/share/cadence/src/carla_control.py "$@" | |||
| exec $PYTHON $INSTALL_PREFIX/share/carla/carla_control.py "$@" | |||
| @@ -2,7 +2,7 @@ | |||
| # Script to start Carla bridges | |||
| INSTALL_PREFIX="X-PREFIX-X" | |||
| CADENCE_PREFIX="$INSTALL_PREFIX"/lib/cadence | |||
| CARLA_PREFIX="$INSTALL_PREFIX"/lib/carla | |||
| # ---------------------------------------------------------------------- | |||
| # Check for enough arguments | |||
| @@ -58,17 +58,17 @@ if [ $RUN_ARCH == "win64" ]; then | |||
| fi | |||
| # ---------------------------------------------------------------------- | |||
| # Check for existing cadence folder | |||
| # Check for existing carla folder | |||
| if [ ! -d $CADENCE_PREFIX ]; then | |||
| echo "$0: Cadence folder non-existing, is it installed?" | |||
| if [ ! -d $CARLA_PREFIX ]; then | |||
| echo "$0: Carla folder does not exist, is it installed?" | |||
| exit | |||
| fi | |||
| # ---------------------------------------------------------------------- | |||
| # Check for existing arch binary | |||
| CARLA_EXEC="$CADENCE_PREFIX/carla-bridge-$RUN_ARCH" | |||
| CARLA_EXEC="$CARLA_PREFIX/carla-bridge-$RUN_ARCH" | |||
| if [ ! -f $CARLA_EXEC ]; then | |||
| echo "$0: Invalid arch (may not be installed)" | |||
| @@ -0,0 +1,62 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for carla libs # | |||
| # ----------------------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| all: | |||
| # -------------------------------------------------------------- | |||
| jackbridge-win32.dll: | |||
| $(MAKE) -C jackbridge win32 | |||
| jackbridge-win64.dll: | |||
| $(MAKE) -C jackbridge win64 | |||
| jackbridge-win32.dll.so: | |||
| $(MAKE) -C jackbridge wine32 | |||
| jackbridge-win64.dll.so: | |||
| $(MAKE) -C jackbridge wine64 | |||
| # -------------------------------------------------------------- | |||
| lilv.a: | |||
| $(MAKE) -C lilv | |||
| lilv_posix32.a: | |||
| $(MAKE) -C lilv posix32 | |||
| lilv_posix64.a: | |||
| $(MAKE) -C lilv posix64 | |||
| lilv_win32.a: | |||
| $(MAKE) -C lilv win32 | |||
| lilv_win64.a: | |||
| $(MAKE) -C lilv win64 | |||
| # -------------------------------------------------------------- | |||
| rtmempool.a: | |||
| $(MAKE) -C rtmempool | |||
| rtmempool_posix32.a: | |||
| $(MAKE) -C rtmempool posix32 | |||
| rtmempool_posix64.a: | |||
| $(MAKE) -C rtmempool posix64 | |||
| rtmempool_win32.a: | |||
| $(MAKE) -C rtmempool win32 | |||
| rtmempool_win64.a: | |||
| $(MAKE) -C rtmempool win64 | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| rm -f *.a *.def *.dll *.so | |||
| $(MAKE) clean -C lilv | |||
| $(MAKE) clean -C rtmempool | |||
| @@ -0,0 +1,51 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for jackbridge # | |||
| # ------------------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| include ../../Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| WINECC ?= winegcc | |||
| BUILD_C_FLAGS += -DJACKBRIDGE_EXPORT -I. | |||
| LINK_FLAGS += -shared | |||
| WIN_BUILD_FLAGS = $(BUILD_C_FLAGS) -DJACKBRIDGE_DUMMY -w | |||
| WIN_32BIT_FLAGS = $(32BIT_FLAGS) | |||
| WIN_64BIT_FLAGS = $(64BIT_FLAGS) | |||
| WIN_LINK_FLAGS = $(LINK_FLAGS) | |||
| WINE_BUILD_FLAGS = $(BUILD_C_FLAGS) -fPIC | |||
| WINE_32BIT_FLAGS = $(32BIT_FLAGS) -L/usr/lib32/wine -L/usr/lib/i386-linux-gnu/wine | |||
| WINE_64BIT_FLAGS = $(64BIT_FLAGS) -L/usr/lib64/wine -L/usr/lib/x86_64-linux-gnu/wine | |||
| WINE_LINK_FLAGS = $(LINK_FLAGS) $(shell pkg-config --libs jack) -ldl | |||
| OBJS = jackbridge.c | |||
| # -------------------------------------------------------------- | |||
| all: | |||
| win32: ../jackbridge-win32.dll | |||
| win64: ../jackbridge-win64.dll | |||
| wine32: ../jackbridge-win32.dll.so | |||
| wine64: ../jackbridge-win64.dll.so | |||
| # -------------------------------------------------------------- | |||
| ../jackbridge-win32.dll: $(OBJS) | |||
| $(CC) $^ $(WIN_BUILD_FLAGS) $(WIN_32BIT_FLAGS) $(WIN_LINK_FLAGS) -Wl,--output-def,$@.def,--out-implib,$@.a -o $@ && $(STRIP) $@ | |||
| ../jackbridge-win64.dll: $(OBJS) | |||
| $(CC) $^ $(WIN_BUILD_FLAGS) $(WIN_64BIT_FLAGS) $(WIN_LINK_FLAGS) -Wl,--output-def,$@.def,--out-implib,$@.a -o $@ && $(STRIP) $@ | |||
| ../jackbridge-win32.dll.so: $(OBJS) ../jackbridge-win32.dll.def | |||
| $(WINECC) $^ $(WINE_BUILD_FLAGS) $(WINE_32BIT_FLAGS) $(WINE_LINK_FLAGS) -mno-cygwin -o $@ && $(STRIP) $@ | |||
| ../jackbridge-win64.dll.so: $(OBJS) ../jackbridge-win64.dll.def | |||
| $(WINECC) $^ $(WINE_BUILD_FLAGS) $(WINE_64BIT_FLAGS) $(WINE_LINK_FLAGS) -mno-cygwin -o $@ && $(STRIP) $@ | |||
| # -------------------------------------------------------------- | |||
| @@ -0,0 +1,259 @@ | |||
| /* | |||
| * JackBridge | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation; either version 2 of the License, or | |||
| * any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the COPYING file | |||
| */ | |||
| #include "jackbridge.h" | |||
| // ----------------------------------------------------------------------------- | |||
| jack_client_t* jackbridge_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_client_open(client_name, options, status); | |||
| #else | |||
| return NULL; | |||
| #endif | |||
| } | |||
| int jackbridge_client_close(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_client_close(client); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_client_name_size() | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_client_name_size(); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| char* jackbridge_get_client_name(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_get_client_name(client); | |||
| #else | |||
| return NULL; | |||
| #endif | |||
| } | |||
| int jackbridge_port_name_size() | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_port_name_size(); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_recompute_total_latencies(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_recompute_total_latencies(client); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| void jackbridge_port_get_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| jack_port_get_latency_range(port, mode, range); | |||
| #endif | |||
| } | |||
| void jackbridge_port_set_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| jack_port_set_latency_range(port, mode, range); | |||
| #endif | |||
| } | |||
| int jackbridge_activate(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_activate(client); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_deactivate(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_deactivate(client); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown_callback, void* arg) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| jack_on_shutdown(client, shutdown_callback, arg); | |||
| #endif | |||
| } | |||
| int jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback latency_callback, void* arg) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_set_latency_callback(client, latency_callback, arg); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_set_process_callback(client, process_callback, arg); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallback freewheel_callback, void* arg) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_set_freewheel_callback(client, freewheel_callback, arg); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback bufsize_callback, void* arg) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_set_buffer_size_callback(client, bufsize_callback, arg); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback srate_callback, void* arg) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_set_sample_rate_callback(client, srate_callback, arg); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_get_sample_rate(client); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_get_buffer_size(client); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_port_register(client, port_name, port_type, flags, buffer_size); | |||
| #else | |||
| return NULL; | |||
| #endif | |||
| } | |||
| int jackbridge_port_unregister(jack_client_t* client, jack_port_t* port) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_port_unregister(client, port); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_port_get_buffer(port, nframes); | |||
| #else | |||
| return NULL; | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------------- | |||
| uint32_t jackbridge_midi_get_event_count(void* port_buffer) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_midi_get_event_count(port_buffer); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| int jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_midi_event_get(event, port_buffer, event_index); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| void jackbridge_midi_clear_buffer(void* port_buffer) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| jack_midi_clear_buffer(port_buffer); | |||
| #endif | |||
| } | |||
| jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_midi_event_reserve(port_buffer, time, data_size); | |||
| #else | |||
| return NULL; | |||
| #endif | |||
| } | |||
| int jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_midi_event_write(port_buffer, time, data, data_size); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| // ----------------------------------------------------------------------------- | |||
| jack_transport_state_t jackbridge_transport_query(const jack_client_t* client, jack_position_t* pos) | |||
| { | |||
| #ifndef JACKBRIDGE_DUMMY | |||
| return jack_transport_query(client, pos); | |||
| #else | |||
| return JackTransportStopped; | |||
| #endif | |||
| } | |||
| @@ -0,0 +1,111 @@ | |||
| /* | |||
| * JackBridge | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation; either version 2 of the License, or | |||
| * any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the COPYING file | |||
| */ | |||
| #ifndef __JACKBRIDGE_H__ | |||
| #define __JACKBRIDGE_H__ | |||
| #if defined(_WIN32) && ! defined(__WINE__) | |||
| # include <stdint.h> | |||
| # include <windows.h> | |||
| # define BRIDGE_EXPORT __declspec (dllexport) | |||
| #else | |||
| # define BRIDGE_EXPORT __attribute__ ((visibility("default"))) | |||
| #endif | |||
| #ifdef __WINE__ | |||
| # define GNU_WIN32 // fix jack_native_thread_t | |||
| #endif | |||
| #include <jack/jack.h> | |||
| #include <jack/midiport.h> | |||
| #include <jack/transport.h> | |||
| #ifdef JACKBRIDGE_EXPORT | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| BRIDGE_EXPORT jack_client_t* jackbridge_client_open(const char* client_name, jack_options_t options, jack_status_t* status, ...); | |||
| BRIDGE_EXPORT int jackbridge_client_close(jack_client_t* client); | |||
| BRIDGE_EXPORT int jackbridge_client_name_size(); | |||
| BRIDGE_EXPORT char* jackbridge_get_client_name(jack_client_t* client); | |||
| BRIDGE_EXPORT int jackbridge_port_name_size(); | |||
| BRIDGE_EXPORT int jackbridge_recompute_total_latencies(jack_client_t* client); | |||
| BRIDGE_EXPORT void jackbridge_port_get_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range); | |||
| BRIDGE_EXPORT void jackbridge_port_set_latency_range(jack_port_t* port, jack_latency_callback_mode_t mode, jack_latency_range_t* range); | |||
| BRIDGE_EXPORT int jackbridge_activate(jack_client_t* client); | |||
| BRIDGE_EXPORT int jackbridge_deactivate(jack_client_t* client); | |||
| BRIDGE_EXPORT void jackbridge_on_shutdown(jack_client_t* client, JackShutdownCallback shutdown_callback, void* arg); | |||
| BRIDGE_EXPORT int jackbridge_set_latency_callback(jack_client_t* client, JackLatencyCallback latency_callback, void* arg); | |||
| BRIDGE_EXPORT int jackbridge_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg); | |||
| BRIDGE_EXPORT int jackbridge_set_freewheel_callback(jack_client_t* client, JackFreewheelCallback freewheel_callback, void* arg); | |||
| BRIDGE_EXPORT int jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCallback bufsize_callback, void* arg); | |||
| BRIDGE_EXPORT int jackbridge_set_sample_rate_callback(jack_client_t* client, JackSampleRateCallback srate_callback, void* arg); | |||
| BRIDGE_EXPORT jack_nframes_t jackbridge_get_sample_rate(jack_client_t* client); | |||
| BRIDGE_EXPORT jack_nframes_t jackbridge_get_buffer_size(jack_client_t* client); | |||
| BRIDGE_EXPORT jack_port_t* jackbridge_port_register(jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); | |||
| BRIDGE_EXPORT int jackbridge_port_unregister(jack_client_t* client, jack_port_t* port); | |||
| BRIDGE_EXPORT void* jackbridge_port_get_buffer(jack_port_t* port, jack_nframes_t nframes); | |||
| BRIDGE_EXPORT uint32_t jackbridge_midi_get_event_count(void* port_buffer); | |||
| BRIDGE_EXPORT int jackbridge_midi_event_get(jack_midi_event_t* event, void* port_buffer, uint32_t event_index); | |||
| BRIDGE_EXPORT void jackbridge_midi_clear_buffer(void* port_buffer); | |||
| BRIDGE_EXPORT jack_midi_data_t* jackbridge_midi_event_reserve(void* port_buffer, jack_nframes_t time, size_t data_size); | |||
| BRIDGE_EXPORT int jackbridge_midi_event_write(void* port_buffer, jack_nframes_t time, const jack_midi_data_t* data, size_t data_size); | |||
| BRIDGE_EXPORT jack_transport_state_t jackbridge_transport_query(const jack_client_t* client, jack_position_t* pos); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #else // JACKBRIDGE_EXPORT | |||
| #define jackbridge_client_open jack_client_open | |||
| #define jackbridge_client_close jack_client_close | |||
| #define jackbridge_client_name_size jack_client_name_size | |||
| #define jackbridge_get_client_name jack_get_client_name | |||
| #define jackbridge_port_name_size jack_port_name_size | |||
| #define jackbridge_recompute_total_latencies jack_recompute_total_latencies | |||
| #define jackbridge_port_get_latency_range jack_port_get_latency_range | |||
| #define jackbridge_port_set_latency_range jack_port_set_latency_range | |||
| #define jackbridge_activate jack_activate | |||
| #define jackbridge_deactivate jack_deactivate | |||
| #define jackbridge_on_shutdown jack_on_shutdown | |||
| #define jackbridge_set_latency_callback jack_set_latency_callback | |||
| #define jackbridge_set_process_callback jack_set_process_callback | |||
| #define jackbridge_set_freewheel_callback jack_set_freewheel_callback | |||
| #define jackbridge_set_buffer_size_callback jack_set_buffer_size_callback | |||
| #define jackbridge_set_sample_rate_callback jack_set_sample_rate_callback | |||
| #define jackbridge_get_sample_rate jack_get_sample_rate | |||
| #define jackbridge_get_buffer_size jack_get_buffer_size | |||
| #define jackbridge_port_register jack_port_register | |||
| #define jackbridge_port_unregister jack_port_unregister | |||
| #define jackbridge_port_get_buffer jack_port_get_buffer | |||
| #define jackbridge_midi_get_event_count jack_midi_get_event_count | |||
| #define jackbridge_midi_event_get jack_midi_event_get | |||
| #define jackbridge_midi_clear_buffer jack_midi_clear_buffer | |||
| #define jackbridge_midi_event_reserve jack_midi_event_reserve | |||
| #define jackbridge_midi_event_write jack_midi_event_write | |||
| #define jackbridge_transport_query jack_transport_query | |||
| #endif // JACKBRIDGE_EXPORT | |||
| #endif // __JACKBRIDGE_H__ | |||
| @@ -0,0 +1,96 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for lilv # | |||
| # ----------------------- # | |||
| # Created by falkTX | |||
| # | |||
| include ../../Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| SERD_VERSION = 0.18.2 | |||
| SORD_VERSION = 0.10.4 | |||
| SRATOM_VERSION = 0.4.0 | |||
| LILV_VERSION = 0.14.4 | |||
| BUILD_C_FLAGS += -fvisibility=hidden -fPIC -w | |||
| BUILD_C_FLAGS += -Iconfig -I../../includes | |||
| OBJS = serd.o sord.o sratom.o lilv.o | |||
| OBJS_posix32 = serd_posix32.o sord_posix32.o sratom_posix32.o lilv_posix32.o | |||
| OBJS_posix64 = serd_posix64.o sord_posix64.o sratom_posix64.o lilv_posix64.o | |||
| OBJS_win32 = serd_win32.o sord_win32.o sratom_win32.o lilv_win32.o | |||
| OBJS_win64 = serd_win64.o sord_win64.o sratom_win64.o lilv_win64.o | |||
| # -------------------------------------------------------------- | |||
| all: ../lilv.a | |||
| posix32: ../lilv_posix32.a | |||
| posix64: ../lilv_posix64.a | |||
| win32: ../lilv_win32.a | |||
| win64: ../lilv_win64.a | |||
| # -------------------------------------------------------------- | |||
| ../lilv.a: $(OBJS) | |||
| $(AR) rs $@ $^ | |||
| ../lilv_posix32.a: $(OBJS_posix32) | |||
| $(AR) rs $@ $^ | |||
| ../lilv_posix64.a: $(OBJS_posix64) | |||
| $(AR) rs $@ $^ | |||
| ../lilv_win32.a: $(OBJS_win32) | |||
| $(AR) rs $@ $^ | |||
| ../lilv_win64.a: $(OBJS_win64) | |||
| $(AR) rs $@ $^ | |||
| # -------------------------------------------------------------- | |||
| serd.o: serd.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) -c -o $@ | |||
| sord.o: sord.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src -c -o $@ | |||
| sratom.o: sratom.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) -c -o $@ | |||
| lilv.o: lilv.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| serd_%32.o: serd.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) $(32BIT_FLAGS) -c -o $@ | |||
| sord_%32.o: sord.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src $(32BIT_FLAGS) -c -o $@ | |||
| sratom_%32.o: sratom.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) $(32BIT_FLAGS) -c -o $@ | |||
| lilv_%32.o: lilv.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src $(32BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| serd_%64.o: serd.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Iserd-$(SERD_VERSION) $(64BIT_FLAGS) -c -o $@ | |||
| sord_%64.o: sord.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Isord-$(SORD_VERSION) -Isord-$(SORD_VERSION)/src $(64BIT_FLAGS) -c -o $@ | |||
| sratom_%64.o: sratom.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Isratom-$(SRATOM_VERSION) $(64BIT_FLAGS) -c -o $@ | |||
| lilv_%64.o: lilv.c | |||
| $(CC) $< $(BUILD_C_FLAGS) -Ililv-$(LILV_VERSION) -Ililv-$(LILV_VERSION)/src $(64BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| rm -f *.o | |||
| @@ -0,0 +1,35 @@ | |||
| #ifndef _LILV_CONFIG_H_ | |||
| #define _LILV_CONFIG_H_ | |||
| #define LILV_VERSION "0.14.4" | |||
| #define LILV_NEW_LV2 1 | |||
| #define HAVE_LV2 1 | |||
| #define HAVE_SERD 1 | |||
| #define HAVE_SORD 1 | |||
| #define HAVE_SRATOM 1 | |||
| #define HAVE_FILENO 1 | |||
| #define HAVE_CLOCK_GETTIME 1 | |||
| #ifdef __WIN32__ | |||
| #define LILV_PATH_SEP ";" | |||
| #define LILV_DIR_SEP "\\" | |||
| #else | |||
| #define LILV_PATH_SEP ":" | |||
| #define LILV_DIR_SEP "/" | |||
| #define HAVE_FLOCK 1 | |||
| #endif | |||
| #if defined(__APPLE__) | |||
| #define LILV_DEFAULT_LV2_PATH "~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2:/Library/Audio/Plug-Ins/LV2" | |||
| #elif defined(__HAIKU__) | |||
| #define LILV_DEFAULT_LV2_PATH "~/.lv2:/boot/common/add-ons/lv2" | |||
| #elif defined(__WIN32__) | |||
| #define LILV_DEFAULT_LV2_PATH "%APPDATA%\\LV2;%COMMONPROGRAMFILES%\\LV2" | |||
| #else | |||
| #define LILV_DEFAULT_LV2_PATH "~/.lv2:/usr/lib/lv2:/usr/local/lib/lv2" | |||
| #endif | |||
| #endif /* _LILV_CONFIG_H_ */ | |||
| @@ -0,0 +1,21 @@ | |||
| #ifndef _SERD_CONFIG_H_ | |||
| #define _SERD_CONFIG_H_ | |||
| #define SERD_VERSION "0.18.2" | |||
| #if defined(__APPLE__) || defined(__HAIKU__) | |||
| #define HAVE_FMAX 1 | |||
| #define HAVE_FILENO 1 | |||
| #define HAVE_POSIX_MEMALIGN 1 | |||
| #elif defined(__WIN32__) | |||
| #define HAVE_FMAX 1 | |||
| #define HAVE_FILENO 1 | |||
| #else | |||
| #define HAVE_FMAX 1 | |||
| #define HAVE_FILENO 1 | |||
| #define HAVE_POSIX_MEMALIGN 1 | |||
| #define HAVE_POSIX_FADVISE 1 | |||
| #endif | |||
| #endif /* _SERD_CONFIG_H_ */ | |||
| @@ -0,0 +1,10 @@ | |||
| #ifndef _SORD_CONFIG_H_ | |||
| #define _SORD_CONFIG_H_ | |||
| #define SORD_VERSION "0.10.4" | |||
| #define HAVE_SERD 1 | |||
| #define HAVE_PCRE 1 | |||
| #endif /* _SORD_CONFIG_H_ */ | |||
| @@ -0,0 +1,11 @@ | |||
| #ifndef _SRATOM_CONFIG_H_ | |||
| #define _SRATOM_CONFIG_H_ | |||
| #define SRATOM_VERSION "0.4.0" | |||
| #define HAVE_LV2 1 | |||
| #define HAVE_SERD 1 | |||
| #define HAVE_SORD 1 | |||
| #endif /* _SRATOM_CONFIG_H_ */ | |||
| @@ -0,0 +1,20 @@ | |||
| diff -U 3 -H -b -B -d -r -N -- lilv-0.14.4.old/src/util.c lilv-0.14.4/src/util.c | |||
| --- lilv-0.14.4.old/src/util.c 2012-08-09 21:51:00.000000000 +0100 | |||
| +++ lilv-0.14.4/src/util.c 2012-09-15 01:20:07.908701939 +0100 | |||
| @@ -29,7 +29,6 @@ | |||
| #include <string.h> | |||
| #ifdef _WIN32 | |||
| -# define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */ | |||
| # include <windows.h> | |||
| # include <direct.h> | |||
| # include <io.h> | |||
| @@ -426,7 +425,7 @@ | |||
| int ret = 0; | |||
| if (strcmp(oldpath, newpath)) { | |||
| #ifdef _WIN32 | |||
| - ret = !CreateSymbolicLink(newpath, oldpath, 0); | |||
| + ret = 0; | |||
| #else | |||
| ret = symlink(oldpath, newpath); | |||
| #endif | |||
| @@ -0,0 +1,137 @@ | |||
| diff -U 3 -H -d -r -N -- lilv-0.14.4.old/lilv/lilv.h lilv-0.14.4/lilv/lilv.h | |||
| --- lilv-0.14.4.old/lilv/lilv.h 2012-09-07 19:00:48.464571333 +0100 | |||
| +++ lilv-0.14.4/lilv/lilv.h 2012-09-07 18:54:00.626548936 +0100 | |||
| @@ -1693,6 +1693,25 @@ | |||
| lilv_ui_get_binary_uri(const LilvUI* ui); | |||
| /** | |||
| + Custom calls | |||
| +*/ | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_supported_features(const LilvUI* ui); | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_required_features(const LilvUI* ui); | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_optional_features(const LilvUI* ui); | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_extension_data(const LilvUI* ui); | |||
| + | |||
| +/** | |||
| @} | |||
| @} | |||
| */ | |||
| diff -U 3 -H -d -r -N -- lilv-0.14.4.old/lilv/lilvmm.hpp lilv-0.14.4/lilv/lilvmm.hpp | |||
| --- lilv-0.14.4.old/lilv/lilvmm.hpp 2012-07-18 02:42:43.000000000 +0100 | |||
| +++ lilv-0.14.4/lilv/lilvmm.hpp 2012-09-07 18:53:14.134318379 +0100 | |||
| @@ -136,6 +136,7 @@ | |||
| struct Nodes { | |||
| LILV_WRAP_COLL(Nodes, Node, nodes); | |||
| LILV_WRAP1(bool, nodes, contains, const Node, node); | |||
| + LILV_WRAP0(Node, nodes, get_first); | |||
| }; | |||
| struct Port { | |||
| @@ -167,6 +168,26 @@ | |||
| const LilvPort* me; | |||
| }; | |||
| +struct UI { | |||
| + inline UI(const LilvUI* c_obj) : me(c_obj) {} | |||
| + LILV_WRAP_CONVERSION(const LilvUI); | |||
| + | |||
| + LILV_WRAP0(Node, ui, get_uri); | |||
| + LILV_WRAP1(bool, ui, is_a, LilvNode*, ui_class); | |||
| + LILV_WRAP0(Node, ui, get_bundle_uri); | |||
| + LILV_WRAP0(Node, ui, get_binary_uri); | |||
| + LILV_WRAP0(Nodes, ui, get_supported_features); | |||
| + LILV_WRAP0(Nodes, ui, get_required_features); | |||
| + LILV_WRAP0(Nodes, ui, get_optional_features); | |||
| + LILV_WRAP0(Nodes, ui, get_extension_data); | |||
| + | |||
| + const LilvUI* me; | |||
| +}; | |||
| + | |||
| +struct UIs { | |||
| + LILV_WRAP_COLL(UIs, UI, uis); | |||
| +}; | |||
| + | |||
| struct Plugin { | |||
| inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {} | |||
| LILV_WRAP_CONVERSION(const LilvPlugin); | |||
| @@ -190,6 +211,8 @@ | |||
| LILV_WRAP0(Node, plugin, get_author_email); | |||
| LILV_WRAP0(Node, plugin, get_author_homepage); | |||
| LILV_WRAP0(bool, plugin, is_replaced); | |||
| + LILV_WRAP0(Nodes, plugin, get_extension_data); | |||
| + LILV_WRAP0(UIs, plugin, get_uis); | |||
| inline Port get_port_by_index(unsigned index) { | |||
| return Port(me, lilv_plugin_get_port_by_index(me, index)); | |||
| diff -U 3 -H -d -r -N -- lilv-0.14.4.old/src/ui.c lilv-0.14.4/src/ui.c | |||
| --- lilv-0.14.4.old/src/ui.c 2012-09-07 19:00:48.464571333 +0100 | |||
| +++ lilv-0.14.4/src/ui.c 2012-09-07 18:59:51.652289664 +0100 | |||
| @@ -128,3 +128,57 @@ | |||
| assert(ui->binary_uri); | |||
| return ui->binary_uri; | |||
| } | |||
| + | |||
| +static LilvNodes* | |||
| +lilv_ui_get_value_internal(const LilvUI* ui, | |||
| + const SordNode* predicate) | |||
| +{ | |||
| + return lilv_world_query_values_internal( | |||
| + ui->world, ui->uri->node, predicate, NULL); | |||
| +} | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_supported_features(const LilvUI* ui) | |||
| +{ | |||
| + LilvNodes* optional = lilv_ui_get_optional_features(ui); | |||
| + LilvNodes* required = lilv_ui_get_required_features(ui); | |||
| + LilvNodes* result = lilv_nodes_new(); | |||
| + | |||
| + LILV_FOREACH(nodes, i, optional) | |||
| + zix_tree_insert((ZixTree*)result, | |||
| + lilv_node_duplicate(lilv_nodes_get(optional, i)), | |||
| + NULL); | |||
| + LILV_FOREACH(nodes, i, required) | |||
| + zix_tree_insert((ZixTree*)result, | |||
| + lilv_node_duplicate(lilv_nodes_get(required, i)), | |||
| + NULL); | |||
| + | |||
| + lilv_nodes_free(optional); | |||
| + lilv_nodes_free(required); | |||
| + | |||
| + return result; | |||
| +} | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_required_features(const LilvUI* ui) | |||
| +{ | |||
| + return lilv_ui_get_value_internal( | |||
| + ui, ui->world->uris.lv2_requiredFeature); | |||
| +} | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_optional_features(const LilvUI* ui) | |||
| +{ | |||
| + return lilv_ui_get_value_internal( | |||
| + ui, ui->world->uris.lv2_optionalFeature); | |||
| +} | |||
| + | |||
| +LILV_API | |||
| +LilvNodes* | |||
| +lilv_ui_get_extension_data(const LilvUI* ui) | |||
| +{ | |||
| + return lilv_ui_get_value_internal(ui, ui->world->uris.lv2_extensionData); | |||
| +} | |||
| @@ -0,0 +1,11 @@ | |||
| diff -U 3 -H -b -B -d -r -N -- lilv-0.14.4.old/lilv/lilvmm.hpp lilv-0.14.4/lilv/lilvmm.hpp | |||
| --- lilv-0.14.4.old/lilv/lilvmm.hpp 2012-09-13 12:47:55.000000000 +0100 | |||
| +++ lilv-0.14.4/lilv/lilvmm.hpp 2012-09-13 12:48:10.950555311 +0100 | |||
| @@ -60,6 +60,7 @@ | |||
| #endif | |||
| struct Node { | |||
| + inline Node(LilvNode* node) : me(node) {} | |||
| inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {} | |||
| inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {} | |||
| @@ -0,0 +1,8 @@ | |||
| Author: | |||
| David Robillard <d@drobilla.net> | |||
| GTK2 GUI and I18N support: | |||
| Lars Luthman <lars.luthman@gmail.com> | |||
| Dynamic manifest support: | |||
| Stefano D'Angelo | |||
| @@ -0,0 +1,13 @@ | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| @@ -0,0 +1,59 @@ | |||
| Installation Instructions | |||
| ========================= | |||
| Basic Installation | |||
| ------------------ | |||
| Building this software requires only Python. To install with default options: | |||
| ./waf configure | |||
| ./waf | |||
| ./waf install | |||
| You may need to become root for the install stage, for example: | |||
| sudo ./waf install | |||
| Configuration Options | |||
| --------------------- | |||
| All supported options can be viewed using the command: | |||
| ./waf --help | |||
| Most options only need to be passed during the configure stage, for example: | |||
| ./waf configure --prefix=/usr | |||
| ./waf | |||
| ./waf install | |||
| Compiler Configuration | |||
| ---------------------- | |||
| Several standard environment variables can be used to control how compilers are | |||
| invoked: | |||
| * CC: Path to C compiler | |||
| * CFLAGS: C compiler options | |||
| * CXX: Path to C++ compiler | |||
| * CXXFLAGS: C++ compiler options | |||
| * CPPFLAGS: C preprocessor options | |||
| * LINKFLAGS: Linker options | |||
| Installation Directories | |||
| ------------------------ | |||
| The --prefix option (or the PREFIX environment variable) can be used to change | |||
| the prefix which all files are installed under. There are also several options | |||
| allowing for more fine-tuned control, see the --help output for details. | |||
| Packaging | |||
| --------- | |||
| Everything can be installed to a specific root directory by passing a --destdir | |||
| option to the install stage (or setting the DESTDIR environment variable), | |||
| which adds a prefix to all install paths. For example: | |||
| ./waf configure --prefix=/usr | |||
| ./waf | |||
| ./waf install --destdir=/tmp/package | |||
| @@ -0,0 +1,90 @@ | |||
| lilv (0.14.4) stable; | |||
| * Deprecate old flawed Lilv::Instance constructors | |||
| * Fix documentation for ui_type parameter of lilv_ui_is_supported() | |||
| * Fix crash when lv2info is run with an invalid URI argument | |||
| * Gracefully handle failure to save plugin state and print error message | |||
| * Reduce memory usage (per node) | |||
| * Simpler node implementation always backed by a SordNode | |||
| * Make all 'zix' symbols private to avoid symbol clashes in static builds | |||
| * Add lv2bench utility | |||
| * Fix various hyper-strict warnings | |||
| * Do not require a C++ compiler to build | |||
| * Add option to build utilities as static binaries | |||
| * Upgrade to waf 1.7.2 | |||
| * lilvmm.hpp: Make Lilv::Instance handle features and failed instantiations | |||
| * lilvmm.hpp: Add Lilv::Instance::get_handle() | |||
| * lilvmm.hpp: Add Lilv::Instance::get_extension_data() | |||
| -- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 01:38:29 -0400 | |||
| lilv (0.14.2) stable; | |||
| * Fix dynmanifest support | |||
| -- David Robillard <d@drobilla.net> Thu, 19 Apr 2012 16:11:31 -0400 | |||
| lilv (0.14.0) stable; | |||
| * Add lilv_plugin_get_extension_data | |||
| * Use path variables in pkgconfig files | |||
| * Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||
| * Make Lilv::uri_to_path static inline (fix linking errors) | |||
| * Use correct URI for dcterms:replaces (for hiding old plugins): | |||
| "http://purl.org/dc/terms/replaces" | |||
| * Fix compilation on BSD | |||
| * Only load dynmanifest libraries once per bundle, not once per plugin | |||
| * Fix lilv_world_find_nodes to work with wildcard subjects | |||
| * Add lilv_plugin_get_related to get resources related to plugins that | |||
| are not directly rdfs:seeAlso linked (e.g. presets) | |||
| * Add lilv_world_load_resource for related resources (e.g. presets) | |||
| * Print presets in lv2info | |||
| * Remove locale smashing kludges and use new serd functions for converting | |||
| nodes to/from numbers. | |||
| * Add LilvState API for handling plugin state. This makes it simple to | |||
| save and restore plugin state both in memory and on disk, as well as | |||
| save presets in a host-sharable way since the disk format is identical | |||
| to the LV2 presets format. | |||
| * Update old references to lv2_list (now lv2ls) | |||
| * Support compilation as C++ under MSVC++. | |||
| * Remove use of wordexp. | |||
| * Add lilv_plugin_get_port_by_designation() and lilv_port_get_index() as an | |||
| improved generic alternative to lilv_plugin_get_latency_port_index(). | |||
| * Add lilv_plugin_get_project() and get author information from project if | |||
| it is not given directly on the plugin. | |||
| -- David Robillard <d@drobilla.net> Wed, 18 Apr 2012 20:06:28 -0400 | |||
| lilv (0.5.0) stable; | |||
| * Remove glib dependency | |||
| * Add lv2core as a pkg-config dependency (for lv2.h header include) | |||
| * Obey prefix when installing bash completion script | |||
| * Support integer minimum, maximum, and default port values in | |||
| lilv_plugin_get_port_ranges_float | |||
| * Add ability to build static library | |||
| -- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||
| lilv (0.4.4) stable; | |||
| * Fix building python bindings | |||
| * Make free methods tolerate being called on NULL | |||
| * Remove lv2jack (replaced by jalv) | |||
| * Fix parsing extra plugin data files in other bundles | |||
| * Fix lilv_ui_is_supported when Lilv is built independently | |||
| -- David Robillard <d@drobilla.net> Sat, 11 Jun 2011 11:20:11 -0400 | |||
| lilv (0.4.2) stable; | |||
| * Fix compilation issues on some systems | |||
| * Fix build system Python 3 compatibility | |||
| -- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||
| lilv (0.4.0) stable; | |||
| * Initial version (forked from SLV2) | |||
| -- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 | |||
| @@ -0,0 +1,29 @@ | |||
| This library is designed to allow parallel installation of different major | |||
| versions. To facilitate this, the shared library name, include directory, and | |||
| pkg-config file are suffixed with the major version number of the library. | |||
| For example, if this library was named "foo" and at version 1.x.y: | |||
| /usr/include/foo-1/foo/foo.h | |||
| /usr/lib/foo-1.so.1.x.y | |||
| /usr/lib/pkgconfig/foo-1.pc | |||
| Dependencies check for pkg-config name "foo-1" and will build | |||
| against a compatible version 1, regardless any other installed versions. | |||
| *** IMPORTANT GUIDELINES FOR PACKAGERS *** | |||
| Packages should follow the same conventions as above, i.e. include the major | |||
| version (and only the major version) in the name of the package. Continuing the | |||
| example above, the package(s) would be named foo-1 and foo-1-dev. This way, | |||
| if/when version 2 comes out, it may be installed at the same time as version 1 | |||
| without breaking anything. | |||
| Please do not create packages of this library that do not follow these | |||
| guidelines, you will break things and cause unnecessary headaches. Please do | |||
| not use any number as a suffix other than the actual major version number of the | |||
| upstream source package. | |||
| Because program and documentation names are not versioned, these should be | |||
| included in separate packages which may replace previous versions, since | |||
| there is little use in having parallel installations of them. | |||
| @@ -0,0 +1,11 @@ | |||
| Lilv | |||
| ---- | |||
| Lilv is a library for LV2 hosts intended to make using LV2 Plugins as simple | |||
| as possible (without sacrificing capabilities). | |||
| More information about LV2 plugins can be found at <http://lv2plug.in>. | |||
| More information about Lilv can be found at <http://drobilla.net/software/lilv>. | |||
| -- David Robillard <d@drobilla.net> | |||
| @@ -0,0 +1,38 @@ | |||
| %module lilv | |||
| %{ | |||
| #include "lilv/lilv.h" | |||
| #include "lilv/lilvmm.hpp" | |||
| %} | |||
| %include "lilv/lilv.h" | |||
| %include "lilv/lilvmm.hpp" | |||
| namespace Lilv { | |||
| %extend Plugins { | |||
| %pythoncode %{ | |||
| def __iter__(self): | |||
| class Iterator(object): | |||
| def __init__(self, plugins): | |||
| self.plugins = plugins | |||
| self.iter = plugins.begin() | |||
| def next(self): | |||
| self.iter = self.plugins.next(self.iter) | |||
| if not self.plugins.is_end(self.iter): | |||
| return self.plugins.get(self.iter) | |||
| else: | |||
| raise StopIteration | |||
| return Iterator(self) | |||
| %} | |||
| }; | |||
| %extend Node { | |||
| %pythoncode %{ | |||
| def __str__(self): | |||
| return self.get_turtle_token() | |||
| %} | |||
| }; | |||
| } /* namespace Lilv */ | |||
| @@ -0,0 +1,96 @@ | |||
| #!/usr/bin/env python | |||
| # -*- coding: utf-8 -*- | |||
| import math | |||
| import lilv | |||
| import sys | |||
| import wave | |||
| import numpy | |||
| # Read command line arguments | |||
| if len(sys.argv) != 4: | |||
| print 'USAGE: lv2_apply.py PLUGIN_URI INPUT_WAV OUTPUT_WAV' | |||
| sys.exit(1) | |||
| # Initialise Lilv | |||
| world = lilv.World() | |||
| world.load_all() | |||
| plugin_uri = world.new_uri(sys.argv[1]) | |||
| wav_in_path = sys.argv[2] | |||
| wav_out_path = sys.argv[3] | |||
| # Find plugin | |||
| plugin = world.get_all_plugins().get_by_uri(plugin_uri) | |||
| if not plugin: | |||
| print "Unknown plugin `%s'\n" % plugin_uri | |||
| sys.exit(1) | |||
| lv2_InputPort = world.new_uri(lilv.LILV_URI_INPUT_PORT) | |||
| lv2_OutputPort = world.new_uri(lilv.LILV_URI_OUTPUT_PORT) | |||
| lv2_AudioPort = world.new_uri(lilv.LILV_URI_AUDIO_PORT) | |||
| n_audio_in = plugin.get_num_ports_of_class(lv2_InputPort, lv2_AudioPort) | |||
| n_audio_out = plugin.get_num_ports_of_class(lv2_OutputPort, lv2_AudioPort) | |||
| if n_audio_out == 0: | |||
| print "Plugin has no audio outputs\n" | |||
| sys.exit(1) | |||
| # Open input file | |||
| wav_in = wave.open(wav_in_path, 'r') | |||
| if not wav_in: | |||
| print "Failed to open input `%s'\n" % wav_in_path | |||
| sys.exit(1) | |||
| if wav_in.getnchannels() != n_audio_in: | |||
| print "Input has %d channels, but plugin has %d audio inputs\n" % ( | |||
| wav_in.getnchannels(), n_audio_in) | |||
| sys.exit(1) | |||
| # Open output file | |||
| wav_out = wave.open(wav_out_path, 'w') | |||
| if not wav_out: | |||
| print "Failed to open output `%s'\n" % wav_out_path | |||
| sys.exit(1) | |||
| # Set output file to same format as input (except possibly nchannels) | |||
| wav_out.setparams(wav_in.getparams()) | |||
| wav_out.setnchannels(n_audio_out) | |||
| rate = wav_in.getframerate() | |||
| nframes = wav_in.getnframes() | |||
| # Instantiate plugin | |||
| instance = lilv.Instance(plugin, rate) | |||
| def read_float(wf, nframes): | |||
| wav = wf.readframes(nframes) | |||
| if wf.getsampwidth() == 4: | |||
| wav = wave.struct.unpack("<%ul" % (len(wav) / 4), wav) | |||
| wav = [ i / float(math.pow(2, 32)) for i in wav ] | |||
| elif wf.getsampwidth() == 2: | |||
| wav = wave.struct.unpack("<%uh" % (len(wav) / 2), wav) | |||
| wav = [ i / float(math.pow(2, 16)) for i in wav ] | |||
| else: | |||
| wav = wave.struct.unpack("%uB" % (len(wav)), wav) | |||
| wav = [ s - 128 for s in wav ] | |||
| wav = [ i / float(math.pow(2, 8)) for i in wav ] | |||
| n_channels = wf.getnchannels() | |||
| wavs = [] | |||
| if n_channels > 1: | |||
| for i in xrange(n_channels): | |||
| wavs.append([ wav[j] for j in xrange(0, len(wav), n_channels) ]) | |||
| else: | |||
| wavs = [ wav ] | |||
| return wavs | |||
| in_buf = read_float(wav_in, nframes) | |||
| # TODO: buffer marshaling | |||
| #instance.connect_port(3, in_buf) | |||
| print '%s => %s => %s @ %d Hz' % (wav_in_path, plugin.get_name(), wav_out_path, rate) | |||
| instance.connect_port(3, in_buf) | |||
| @@ -0,0 +1,9 @@ | |||
| #!/usr/bin/env python | |||
| import lilv | |||
| world = lilv.World() | |||
| world.load_all() | |||
| for i in world.get_all_plugins(): | |||
| print(i.get_uri()) | |||
| @@ -0,0 +1,33 @@ | |||
| .TH LV2INFO 1 "8 Jan 2012" | |||
| .SH NAME | |||
| .B lv2info \- print information about an installed LV2 plugin. | |||
| .SH SYNOPSIS | |||
| .B lv2info PLUGIN_URI | |||
| .SH OPTIONS | |||
| .TP | |||
| \fB\-p FILE | |||
| Write Turtle description of plugin to FILE | |||
| .TP | |||
| \fB\-m FILE | |||
| Add record of plugin to manifest FILE | |||
| .TP | |||
| \fB\-\-help\fR | |||
| Display help and exit | |||
| .TP | |||
| \fB\-\-version\fR | |||
| Display version information and exit | |||
| .SH SEE ALSO | |||
| .BR lilv(3), | |||
| .BR lv2ls(1) | |||
| .SH AUTHOR | |||
| lv2info was written by David Robillard <d@drobilla.net> | |||
| .PP | |||
| This manual page was written by Jaromír Mikes <mira.mikes@seznam.cz> | |||
| and David Robillard <d@drobilla.net> | |||
| @@ -0,0 +1,30 @@ | |||
| .TH LV2LS 1 "17 Jan 2012" | |||
| .SH NAME | |||
| .B lv2ls \- List all installed LV2 plugins. | |||
| .SH SYNOPSIS | |||
| .B lv2ls [OPTION]... | |||
| .SH OPTIONS | |||
| .TP | |||
| \fB\-n\fR, \fB\-\-names\fR | |||
| Show names instead of URIs | |||
| .TP | |||
| \fB\-\-help\fR | |||
| Display help and exit | |||
| .TP | |||
| \fB\-\-version\fR | |||
| Display version information and exit | |||
| .SH SEE ALSO | |||
| .BR lilv(3), | |||
| .BR lv2info(1) | |||
| .SH AUTHOR | |||
| lv2ls was written by David Robillard <d@drobilla.net> | |||
| .PP | |||
| This manual page was written by Jaromír Mikes <mira.mikes@seznam.cz> | |||
| and David Robillard <d@drobilla.net> | |||
| @@ -0,0 +1,563 @@ | |||
| body { | |||
| font-size: medium; | |||
| font-family: sans-serif; | |||
| } | |||
| #top { | |||
| background-color: #F3F3F3; | |||
| margin: 0; | |||
| padding: 0; | |||
| border-bottom: 1px solid #DDD; | |||
| margin-bottom: 1ex; | |||
| font-size: xx-large; | |||
| font-weight: bold; | |||
| } | |||
| div.header { | |||
| display: none; | |||
| } | |||
| .tabs { | |||
| display: none; | |||
| } | |||
| h1 h2 h3 h4 h5 h6 { | |||
| font-weight: bold; | |||
| } | |||
| h1 { | |||
| font-size: 164%; | |||
| } | |||
| h2 { | |||
| font-size: 132%; | |||
| } | |||
| h3 { | |||
| font-size: 124%; | |||
| } | |||
| h4 { | |||
| font-size: 116%; | |||
| } | |||
| h5 { | |||
| font-size: 108%; | |||
| } | |||
| h6 { | |||
| font-size: 100%; | |||
| } | |||
| p { | |||
| margin: 0 0 1ex 0; | |||
| } | |||
| br { | |||
| display: none; | |||
| } | |||
| dt { | |||
| font-weight: 700; | |||
| } | |||
| div.multicol { | |||
| } | |||
| p.startli,p.startdd,p.starttd { | |||
| margin-top: 2px; | |||
| } | |||
| p.endli { | |||
| margin-bottom: 0; | |||
| } | |||
| p.enddd { | |||
| margin-bottom: 4px; | |||
| } | |||
| p.endtd { | |||
| margin-bottom: 2px; | |||
| } | |||
| caption { | |||
| font-weight: 700; | |||
| } | |||
| span.legend { | |||
| font-size: 70%; | |||
| text-align: center; | |||
| } | |||
| h3.version { | |||
| font-size: 90%; | |||
| text-align: center; | |||
| } | |||
| div.qindex,div.navtab { | |||
| background-color: #EBEFF6; | |||
| border: 1px solid #A3B4D7; | |||
| text-align: center; | |||
| margin: 2px; | |||
| padding: 2px; | |||
| } | |||
| div.qindex,div.navpath { | |||
| width: 100%; | |||
| line-height: 140%; | |||
| } | |||
| div.navtab { | |||
| margin-right: 15px; | |||
| } | |||
| /* @group Link Styling */ | |||
| a { | |||
| color: #3D8C57; | |||
| text-decoration: none; | |||
| } | |||
| .contents a:visited { | |||
| color: #50755E; | |||
| } | |||
| a:hover { | |||
| text-decoration: underline; | |||
| } | |||
| a.qindexHL { | |||
| background-color: #9CAFD4; | |||
| color: #FFF; | |||
| border: 1px double #869DCA; | |||
| } | |||
| a.code { | |||
| color: #4665A2; | |||
| } | |||
| a.codeRef { | |||
| color: #4665A2; | |||
| } | |||
| /* @end */ | |||
| dl.el { | |||
| margin-left: -1cm; | |||
| } | |||
| .fragment { | |||
| font-family: monospace, fixed; | |||
| font-size: 105%; | |||
| } | |||
| pre.fragment { | |||
| border: 1px solid #C4C4C4; | |||
| background-color: #F9F9F9; | |||
| padding: 4px 6px; | |||
| margin: 4px 8px 4px 2px; | |||
| overflow: auto; | |||
| font-size: 9pt; | |||
| line-height: 125%; | |||
| } | |||
| div.ah { | |||
| background-color: #000; | |||
| font-weight: 700; | |||
| color: #FFF; | |||
| margin-bottom: 3px; | |||
| margin-top: 3px; | |||
| padding: .2em; | |||
| border: thin solid #333; | |||
| } | |||
| div.groupHeader { | |||
| margin-left: 16px; | |||
| margin-top: 12px; | |||
| margin-bottom: 6px; | |||
| font-weight: 700; | |||
| } | |||
| div.groupText { | |||
| margin-left: 16px; | |||
| font-style: italic; | |||
| } | |||
| body { | |||
| background: #FFF; | |||
| color: #000; | |||
| margin: 0; | |||
| } | |||
| div.contents { | |||
| margin-top: 10px; | |||
| margin-left: 10px; | |||
| margin-right: 10px; | |||
| } | |||
| td.indexkey { | |||
| background-color: #EBEFF6; | |||
| font-weight: 700; | |||
| border: 1px solid #C4CFE5; | |||
| margin: 2px 0; | |||
| padding: 2px 10px; | |||
| } | |||
| td.indexvalue { | |||
| background-color: #EBEFF6; | |||
| border: 1px solid #C4CFE5; | |||
| padding: 2px 10px; | |||
| margin: 2px 0; | |||
| } | |||
| tr.memlist { | |||
| background-color: #EEF1F7; | |||
| } | |||
| p.formulaDsp { | |||
| text-align: center; | |||
| } | |||
| img.formulaDsp { | |||
| } | |||
| img.formulaInl { | |||
| vertical-align: middle; | |||
| } | |||
| div.center { | |||
| text-align: center; | |||
| margin-top: 0; | |||
| margin-bottom: 0; | |||
| padding: 0; | |||
| } | |||
| div.center img { | |||
| border: 0; | |||
| } | |||
| address.footer { | |||
| text-align: right; | |||
| padding: 0 0.25em 0.25em 0; | |||
| } | |||
| img.footer { | |||
| border: 0; | |||
| vertical-align: middle; | |||
| } | |||
| /* @group Code Colorization */ | |||
| span.keyword { | |||
| color: green; | |||
| } | |||
| span.keywordtype { | |||
| color: #604020; | |||
| } | |||
| span.keywordflow { | |||
| color: #e08000; | |||
| } | |||
| span.comment { | |||
| color: maroon; | |||
| } | |||
| span.preprocessor { | |||
| color: #806020; | |||
| } | |||
| span.stringliteral { | |||
| color: #002080; | |||
| } | |||
| span.charliteral { | |||
| color: teal; | |||
| } | |||
| span.vhdldigit { | |||
| color: #F0F; | |||
| } | |||
| span.vhdlkeyword { | |||
| color: #700070; | |||
| } | |||
| span.vhdllogic { | |||
| color: red; | |||
| } | |||
| /* @end */ | |||
| td.tiny { | |||
| font-size: 75%; | |||
| } | |||
| .dirtab { | |||
| padding: 4px; | |||
| border-collapse: collapse; | |||
| border: 1px solid #A3B4D7; | |||
| } | |||
| th.dirtab { | |||
| background: #EBEFF6; | |||
| font-weight: 700; | |||
| } | |||
| hr { | |||
| height: 0; | |||
| border: none; | |||
| border-top: 1px solid #DDD; | |||
| margin: 2em 0 1em; | |||
| } | |||
| hr.footer { | |||
| height: 1px; | |||
| } | |||
| /* @group Member Descriptions */ | |||
| table.memberdecls { | |||
| border-spacing: 0; | |||
| font-size: small; | |||
| } | |||
| .mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
| background-color: #FBFBFB; | |||
| margin: 0; | |||
| padding: 0.25ex; | |||
| } | |||
| .mdescLeft,.mdescRight { | |||
| color: #555; | |||
| } | |||
| .memItemLeft,.memItemRight,.memTemplParams { | |||
| border-top: 1px solid #DDD; | |||
| } | |||
| .memItemLeft,.memTemplItemLeft { | |||
| white-space: nowrap; | |||
| padding-left: 2em; | |||
| } | |||
| .memTemplParams { | |||
| color: #464646; | |||
| white-space: nowrap; | |||
| } | |||
| /* @end */ | |||
| /* @group Member Details */ | |||
| /* Styles for detailed member documentation */ | |||
| .memtemplate { | |||
| font-size: 80%; | |||
| color: #4665A2; | |||
| font-weight: bold; | |||
| } | |||
| .memnav { | |||
| background-color: #EBEFF6; | |||
| border: 1px solid #A3B4D7; | |||
| text-align: center; | |||
| margin: 2px; | |||
| margin-right: 15px; | |||
| padding: 2px; | |||
| } | |||
| .memitem { | |||
| padding: 0; | |||
| margin: 1ex 0 2ex 0; | |||
| border: 1px solid #CCC; | |||
| } | |||
| .memname { | |||
| white-space: nowrap; | |||
| font-weight: bold; | |||
| } | |||
| .memproto { | |||
| border-bottom: 1px solid #DDD; | |||
| padding: 0.5ex; | |||
| font-weight: bold; | |||
| background-color: #F3F3F3; | |||
| } | |||
| .memdoc { | |||
| padding: 1ex; | |||
| background-color: #FBFBFB; | |||
| border-top-width: 0; | |||
| } | |||
| .paramkey { | |||
| text-align: right; | |||
| } | |||
| .paramtype { | |||
| white-space: nowrap; | |||
| } | |||
| .paramname { | |||
| color: #602020; | |||
| white-space: nowrap; | |||
| } | |||
| .paramname em { | |||
| font-style: normal; | |||
| } | |||
| /* @end */ | |||
| /* @group Directory (tree) */ | |||
| /* for the tree view */ | |||
| .ftvtree { | |||
| font-family: sans-serif; | |||
| margin: 0; | |||
| } | |||
| /* these are for tree view when used as main index */ | |||
| .directory { | |||
| font-size: 9pt; | |||
| font-weight: bold; | |||
| margin: 5px; | |||
| } | |||
| .directory h3 { | |||
| margin: 0; | |||
| margin-top: 1em; | |||
| font-size: 11pt; | |||
| } | |||
| .directory > h3 { | |||
| margin-top: 0; | |||
| } | |||
| .directory p { | |||
| margin: 0; | |||
| white-space: nowrap; | |||
| } | |||
| .directory div { | |||
| display: none; | |||
| margin: 0; | |||
| } | |||
| .directory img { | |||
| vertical-align: -30%; | |||
| } | |||
| /* these are for tree view when not used as main index */ | |||
| .directory-alt { | |||
| font-size: 100%; | |||
| font-weight: bold; | |||
| } | |||
| .directory-alt h3 { | |||
| margin: 0; | |||
| margin-top: 1em; | |||
| font-size: 11pt; | |||
| } | |||
| .directory-alt > h3 { | |||
| margin-top: 0; | |||
| } | |||
| .directory-alt p { | |||
| margin: 0; | |||
| white-space: nowrap; | |||
| } | |||
| .directory-alt div { | |||
| display: none; | |||
| margin: 0; | |||
| } | |||
| .directory-alt img { | |||
| vertical-align: -30%; | |||
| } | |||
| /* @end */ | |||
| div.dynheader { | |||
| margin-top: 8px; | |||
| } | |||
| address { | |||
| font-style: normal; | |||
| color: #2A3D61; | |||
| } | |||
| table.doxtable { | |||
| border-collapse: collapse; | |||
| margin: 0.5ex; | |||
| } | |||
| table.doxtable td,table.doxtable th { | |||
| border: 1px solid #DDD; | |||
| padding: 3px 7px 2px; | |||
| } | |||
| table.doxtable th { | |||
| background-color: #F3F3F3; | |||
| color: #000; | |||
| padding-bottom: 4px; | |||
| padding-top: 5px; | |||
| text-align: left; | |||
| font-weight: bold; | |||
| } | |||
| .tabsearch { | |||
| top: 0; | |||
| left: 10px; | |||
| height: 36px; | |||
| z-index: 101; | |||
| overflow: hidden; | |||
| font-size: 13px; | |||
| } | |||
| .navpath ul { | |||
| font-size: 11px; | |||
| height: 30px; | |||
| line-height: 30px; | |||
| color: #8AA0CC; | |||
| border: 1px solid #C2CDE4; | |||
| overflow: hidden; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .navpath li { | |||
| list-style-type: none; | |||
| float: left; | |||
| padding-left: 10px; | |||
| padding-right: 15px; | |||
| color: #364D7C; | |||
| } | |||
| .navpath a { | |||
| height: 32px; | |||
| display: block; | |||
| text-decoration: none; | |||
| outline: none; | |||
| } | |||
| .navpath a:hover { | |||
| color: #6884BD; | |||
| } | |||
| div.summary { | |||
| float: right; | |||
| font-size: 8pt; | |||
| padding-right: 5px; | |||
| width: 50%; | |||
| text-align: right; | |||
| } | |||
| div.summary a { | |||
| white-space: nowrap; | |||
| } | |||
| div.header { | |||
| background-color: #F3F3F3; | |||
| margin: 0; | |||
| border-bottom: 1px solid #DDD; | |||
| } | |||
| div.headertitle { | |||
| padding: 5px 5px 5px 10px; | |||
| font-size: 180%; | |||
| font-weight: bold; | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| prefix=@PREFIX@ | |||
| exec_prefix=@EXEC_PREFIX@ | |||
| libdir=@LIBDIR@ | |||
| includedir=@INCLUDEDIR@ | |||
| Name: Lilv | |||
| Version: @LILV_VERSION@ | |||
| Description: Simple C library for hosting LV2 plugins | |||
| Requires: lv2core @LILV_PKG_DEPS@ | |||
| Libs: -L${libdir} -l@LIB_LILV@ -ldl | |||
| Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@ | |||
| @@ -0,0 +1,29 @@ | |||
| @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . | |||
| @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
| @prefix : <http://usefulinc.com/ns/doap#> . | |||
| @prefix foaf: <http://xmlns.com/foaf/0.1/> . | |||
| <http://drobilla.net/software/lilv> | |||
| a :Project ; | |||
| :bug-database <http://dev.drobilla.net/query?status=new&status=assigned&status=reopened&component=LILV&order=priority> ; | |||
| :developer [ | |||
| a foaf:Person ; | |||
| rdfs:seeAlso <http://drobilla.net/drobilla.rdf> ; | |||
| foaf:homepage <http://drobilla.net> ; | |||
| foaf:mbox_sha1sum "253b3c58086250260bac1232d744d150274ad308" ; | |||
| foaf:name "David Robillard" | |||
| ] ; | |||
| :download-page <http://download.drobilla.net> ; | |||
| :homepage <http://drobilla.net/software/lilv> ; | |||
| :license <http://usefulinc.com/doap/licenses/gpl> ; | |||
| :name "LILV" ; | |||
| :programming-language "C", "Turtle" ; | |||
| :repository [ | |||
| :browse <http://dev.drobilla.net/browser/trunk/lilv> ; | |||
| :location <http://svn.drobilla.net/lad/trunk/lilv> ; | |||
| a :SVNRepository | |||
| ] ; | |||
| :shortdesc "Library for simple use of LV2 plugins" ; | |||
| :shortname "LILV" . | |||
| @@ -0,0 +1,332 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef LILV_LILVMM_HPP | |||
| #define LILV_LILVMM_HPP | |||
| #include "lilv/lilv.h" | |||
| #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) | |||
| # define LILV_DEPRECATED __attribute__((__deprecated__)) | |||
| #else | |||
| # define LILV_DEPRECATED | |||
| #endif | |||
| namespace Lilv { | |||
| static inline const char* | |||
| uri_to_path(const char* uri) { | |||
| return lilv_uri_to_path(uri); | |||
| } | |||
| #define LILV_WRAP0(RT, prefix, name) \ | |||
| inline RT name() { return lilv_ ## prefix ## _ ## name (me); } | |||
| #define LILV_WRAP0_VOID(prefix, name) \ | |||
| inline void name() { lilv_ ## prefix ## _ ## name(me); } | |||
| #define LILV_WRAP1(RT, prefix, name, T1, a1) \ | |||
| inline RT name(T1 a1) { return lilv_ ## prefix ## _ ## name (me, a1); } | |||
| #define LILV_WRAP1_VOID(prefix, name, T1, a1) \ | |||
| inline void name(T1 a1) { lilv_ ## prefix ## _ ## name(me, a1); } | |||
| #define LILV_WRAP2(RT, prefix, name, T1, a1, T2, a2) \ | |||
| inline RT name(T1 a1, T2 a2) { \ | |||
| return lilv_ ## prefix ## _ ## name(me, a1, a2); \ | |||
| } | |||
| #define LILV_WRAP2_VOID(prefix, name, T1, a1, T2, a2) \ | |||
| inline void name(T1 a1, T2 a2) { lilv_ ## prefix ## _ ## name(me, a1, a2); } | |||
| #ifndef SWIG | |||
| #define LILV_WRAP_CONVERSION(CT) \ | |||
| inline operator CT*() const { return me; } | |||
| #else | |||
| #define LILV_WRAP_CONVERSION(CT) | |||
| #endif | |||
| struct Node { | |||
| inline Node(LilvNode* node) : me(node) {} | |||
| inline Node(const LilvNode* node) : me(lilv_node_duplicate(node)) {} | |||
| inline Node(const Node& copy) : me(lilv_node_duplicate(copy.me)) {} | |||
| inline ~Node() { lilv_node_free(me); } | |||
| inline bool equals(const Node& other) const { | |||
| return lilv_node_equals(me, other.me); | |||
| } | |||
| inline bool operator==(const Node& other) const { return equals(other); } | |||
| LILV_WRAP_CONVERSION(LilvNode); | |||
| LILV_WRAP0(char*, node, get_turtle_token); | |||
| LILV_WRAP0(bool, node, is_uri); | |||
| LILV_WRAP0(const char*, node, as_uri); | |||
| LILV_WRAP0(bool, node, is_blank); | |||
| LILV_WRAP0(const char*, node, as_blank); | |||
| LILV_WRAP0(bool, node, is_literal); | |||
| LILV_WRAP0(bool, node, is_string); | |||
| LILV_WRAP0(const char*, node, as_string); | |||
| LILV_WRAP0(bool, node, is_float); | |||
| LILV_WRAP0(float, node, as_float); | |||
| LILV_WRAP0(bool, node, is_int); | |||
| LILV_WRAP0(int, node, as_int); | |||
| LILV_WRAP0(bool, node, is_bool); | |||
| LILV_WRAP0(bool, node, as_bool); | |||
| LilvNode* me; | |||
| }; | |||
| struct ScalePoint { | |||
| inline ScalePoint(const LilvScalePoint* c_obj) : me(c_obj) {} | |||
| LILV_WRAP_CONVERSION(const LilvScalePoint); | |||
| LILV_WRAP0(const LilvNode*, scale_point, get_label); | |||
| LILV_WRAP0(const LilvNode*, scale_point, get_value); | |||
| const LilvScalePoint* me; | |||
| }; | |||
| struct PluginClass { | |||
| inline PluginClass(const LilvPluginClass* c_obj) : me(c_obj) {} | |||
| LILV_WRAP_CONVERSION(const LilvPluginClass); | |||
| LILV_WRAP0(Node, plugin_class, get_parent_uri); | |||
| LILV_WRAP0(Node, plugin_class, get_uri); | |||
| LILV_WRAP0(Node, plugin_class, get_label); | |||
| LILV_WRAP0(LilvPluginClasses*, plugin_class, get_children); | |||
| const LilvPluginClass* me; | |||
| }; | |||
| #define LILV_WRAP_COLL(CT, ET, prefix) \ | |||
| inline CT(const Lilv ## CT* c_obj) : me(c_obj) {} \ | |||
| LILV_WRAP_CONVERSION(const Lilv ## CT); \ | |||
| LILV_WRAP0(unsigned, prefix, size); \ | |||
| LILV_WRAP1(const ET, prefix, get, LilvIter*, i); \ | |||
| LILV_WRAP0(LilvIter*, prefix, begin); \ | |||
| LILV_WRAP1(LilvIter*, prefix, next, LilvIter*, i); \ | |||
| LILV_WRAP1(bool, prefix, is_end, LilvIter*, i); \ | |||
| const Lilv ## CT* me; \ | |||
| struct PluginClasses { | |||
| LILV_WRAP_COLL(PluginClasses, PluginClass, plugin_classes); | |||
| LILV_WRAP1(const PluginClass, plugin_classes, | |||
| get_by_uri, const LilvNode*, uri); | |||
| }; | |||
| struct ScalePoints { | |||
| LILV_WRAP_COLL(ScalePoints, ScalePoint, scale_points); | |||
| }; | |||
| struct Nodes { | |||
| LILV_WRAP_COLL(Nodes, Node, nodes); | |||
| LILV_WRAP1(bool, nodes, contains, const Node, node); | |||
| LILV_WRAP0(Node, nodes, get_first); | |||
| }; | |||
| struct Port { | |||
| inline Port(const LilvPlugin* p, const LilvPort* c_obj) | |||
| : parent(p), me(c_obj) | |||
| {} | |||
| LILV_WRAP_CONVERSION(const LilvPort); | |||
| #define LILV_PORT_WRAP0(RT, name) \ | |||
| inline RT name () { return lilv_port_ ## name (parent, me); } | |||
| #define LILV_PORT_WRAP1(RT, name, T1, a1) \ | |||
| inline RT name (T1 a1) { return lilv_port_ ## name (parent, me, a1); } | |||
| LILV_PORT_WRAP1(LilvNodes*, get_value, LilvNode*, predicate); | |||
| LILV_PORT_WRAP0(LilvNodes*, get_properties) | |||
| LILV_PORT_WRAP1(bool, has_property, LilvNode*, property_uri); | |||
| LILV_PORT_WRAP1(bool, supports_event, LilvNode*, event_uri); | |||
| LILV_PORT_WRAP0(const LilvNode*, get_symbol); | |||
| LILV_PORT_WRAP0(LilvNode*, get_name); | |||
| LILV_PORT_WRAP0(const LilvNodes*, get_classes); | |||
| LILV_PORT_WRAP1(bool, is_a, LilvNode*, port_class); | |||
| LILV_PORT_WRAP0(LilvScalePoints*, get_scale_points); | |||
| // TODO: get_range (output parameters) | |||
| const LilvPlugin* parent; | |||
| const LilvPort* me; | |||
| }; | |||
| struct UI { | |||
| inline UI(const LilvUI* c_obj) : me(c_obj) {} | |||
| LILV_WRAP_CONVERSION(const LilvUI); | |||
| LILV_WRAP0(Node, ui, get_uri); | |||
| LILV_WRAP1(bool, ui, is_a, LilvNode*, ui_class); | |||
| LILV_WRAP0(Node, ui, get_bundle_uri); | |||
| LILV_WRAP0(Node, ui, get_binary_uri); | |||
| LILV_WRAP0(Nodes, ui, get_supported_features); | |||
| LILV_WRAP0(Nodes, ui, get_required_features); | |||
| LILV_WRAP0(Nodes, ui, get_optional_features); | |||
| LILV_WRAP0(Nodes, ui, get_extension_data); | |||
| const LilvUI* me; | |||
| }; | |||
| struct UIs { | |||
| LILV_WRAP_COLL(UIs, UI, uis); | |||
| }; | |||
| struct Plugin { | |||
| inline Plugin(const LilvPlugin* c_obj) : me(c_obj) {} | |||
| LILV_WRAP_CONVERSION(const LilvPlugin); | |||
| LILV_WRAP0(bool, plugin, verify); | |||
| LILV_WRAP0(Node, plugin, get_uri); | |||
| LILV_WRAP0(Node, plugin, get_bundle_uri); | |||
| LILV_WRAP0(Nodes, plugin, get_data_uris); | |||
| LILV_WRAP0(Node, plugin, get_library_uri); | |||
| LILV_WRAP0(Node, plugin, get_name); | |||
| LILV_WRAP0(PluginClass, plugin, get_class); | |||
| LILV_WRAP1(Nodes, plugin, get_value, Node, pred); | |||
| LILV_WRAP1(bool, plugin, has_feature, Node, feature_uri); | |||
| LILV_WRAP0(Nodes, plugin, get_supported_features); | |||
| LILV_WRAP0(Nodes, plugin, get_required_features); | |||
| LILV_WRAP0(Nodes, plugin, get_optional_features); | |||
| LILV_WRAP0(unsigned, plugin, get_num_ports); | |||
| LILV_WRAP0(bool, plugin, has_latency); | |||
| LILV_WRAP0(unsigned, plugin, get_latency_port_index); | |||
| LILV_WRAP0(Node, plugin, get_author_name); | |||
| LILV_WRAP0(Node, plugin, get_author_email); | |||
| LILV_WRAP0(Node, plugin, get_author_homepage); | |||
| LILV_WRAP0(bool, plugin, is_replaced); | |||
| LILV_WRAP0(Nodes, plugin, get_extension_data); | |||
| LILV_WRAP0(UIs, plugin, get_uis); | |||
| inline Port get_port_by_index(unsigned index) { | |||
| return Port(me, lilv_plugin_get_port_by_index(me, index)); | |||
| } | |||
| inline Port get_port_by_symbol(LilvNode* symbol) { | |||
| return Port(me, lilv_plugin_get_port_by_symbol(me, symbol)); | |||
| } | |||
| inline void get_port_ranges_float(float* min_values, | |||
| float* max_values, | |||
| float* def_values) { | |||
| return lilv_plugin_get_port_ranges_float( | |||
| me, min_values, max_values, def_values); | |||
| } | |||
| inline unsigned get_num_ports_of_class(LilvNode* class_1, | |||
| LilvNode* class_2) { | |||
| // TODO: varargs | |||
| return lilv_plugin_get_num_ports_of_class(me, class_1, class_2, NULL); | |||
| } | |||
| const LilvPlugin* me; | |||
| }; | |||
| struct Plugins { | |||
| LILV_WRAP_COLL(Plugins, Plugin, plugins); | |||
| LILV_WRAP1(const Plugin, plugins, get_by_uri, const LilvNode*, uri); | |||
| }; | |||
| struct Instance { | |||
| inline Instance(LilvInstance* instance) : me(instance) {} | |||
| LILV_DEPRECATED | |||
| inline Instance(Plugin plugin, double sample_rate) { | |||
| me = lilv_plugin_instantiate(plugin, sample_rate, NULL); | |||
| } | |||
| LILV_DEPRECATED inline Instance(Plugin plugin, | |||
| double sample_rate, | |||
| LV2_Feature* const* features) { | |||
| me = lilv_plugin_instantiate(plugin, sample_rate, features); | |||
| } | |||
| static inline Instance* create(Plugin plugin, | |||
| double sample_rate, | |||
| LV2_Feature* const* features) { | |||
| LilvInstance* me = lilv_plugin_instantiate( | |||
| plugin, sample_rate, features); | |||
| return me ? new Instance(me) : NULL; | |||
| } | |||
| LILV_WRAP_CONVERSION(LilvInstance); | |||
| LILV_WRAP2_VOID(instance, connect_port, | |||
| unsigned, port_index, | |||
| void*, data_location); | |||
| LILV_WRAP0_VOID(instance, activate); | |||
| LILV_WRAP1_VOID(instance, run, unsigned, sample_count); | |||
| LILV_WRAP0_VOID(instance, deactivate); | |||
| inline const void* get_extension_data(const char* uri) { | |||
| return lilv_instance_get_extension_data(me, uri); | |||
| } | |||
| inline const LV2_Descriptor* get_descriptor() { | |||
| return lilv_instance_get_descriptor(me); | |||
| } | |||
| inline LV2_Handle get_handle() { | |||
| return lilv_instance_get_handle(me); | |||
| } | |||
| LilvInstance* me; | |||
| }; | |||
| struct World { | |||
| inline World() : me(lilv_world_new()) {} | |||
| inline ~World() { lilv_world_free(me); } | |||
| inline LilvNode* new_uri(const char* uri) { | |||
| return lilv_new_uri(me, uri); | |||
| } | |||
| inline LilvNode* new_string(const char* str) { | |||
| return lilv_new_string(me, str); | |||
| } | |||
| inline LilvNode* new_int(int val) { | |||
| return lilv_new_int(me, val); | |||
| } | |||
| inline LilvNode* new_float(float val) { | |||
| return lilv_new_float(me, val); | |||
| } | |||
| inline LilvNode* new_bool(bool val) { | |||
| return lilv_new_bool(me, val); | |||
| } | |||
| inline Nodes find_nodes(const LilvNode* subject, | |||
| const LilvNode* predicate, | |||
| const LilvNode* object) { | |||
| return lilv_world_find_nodes(me, subject, predicate, object); | |||
| } | |||
| LILV_WRAP2_VOID(world, set_option, const char*, uri, LilvNode*, value); | |||
| LILV_WRAP0_VOID(world, load_all); | |||
| LILV_WRAP1_VOID(world, load_bundle, LilvNode*, bundle_uri); | |||
| LILV_WRAP0(const LilvPluginClass*, world, get_plugin_class); | |||
| LILV_WRAP0(const LilvPluginClasses*, world, get_plugin_classes); | |||
| LILV_WRAP0(const Plugins, world, get_all_plugins); | |||
| LilvWorld* me; | |||
| }; | |||
| } /* namespace Lilv */ | |||
| #endif /* LILV_LILVMM_HPP */ | |||
| @@ -0,0 +1,214 @@ | |||
| /* | |||
| Copyright 2008-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "lilv_internal.h" | |||
| int | |||
| lilv_ptr_cmp(const void* a, const void* b, void* user_data) | |||
| { | |||
| return (intptr_t)a - (intptr_t)b; | |||
| } | |||
| int | |||
| lilv_resource_node_cmp(const void* a, const void* b, void* user_data) | |||
| { | |||
| const SordNode* an = ((const LilvNode*)a)->node; | |||
| const SordNode* bn = ((const LilvNode*)b)->node; | |||
| return (intptr_t)an - (intptr_t)bn; | |||
| } | |||
| /* Generic collection functions */ | |||
| static inline LilvCollection* | |||
| lilv_collection_new(ZixComparator cmp, ZixDestroyFunc destructor) | |||
| { | |||
| return zix_tree_new(false, cmp, NULL, destructor); | |||
| } | |||
| void | |||
| lilv_collection_free(LilvCollection* coll) | |||
| { | |||
| if (coll) | |||
| zix_tree_free((ZixTree*)coll); | |||
| } | |||
| unsigned | |||
| lilv_collection_size(const LilvCollection* coll) | |||
| { | |||
| return (coll ? zix_tree_size((const ZixTree*)coll) : 0); | |||
| } | |||
| LilvIter* | |||
| lilv_collection_begin(const LilvCollection* collection) | |||
| { | |||
| if (collection) { | |||
| return (LilvIter*)zix_tree_begin((ZixTree*)collection); | |||
| } | |||
| return NULL; | |||
| } | |||
| void* | |||
| lilv_collection_get(const LilvCollection* collection, | |||
| const LilvIter* i) | |||
| { | |||
| return zix_tree_get((ZixTreeIter*)i); | |||
| } | |||
| /* Constructors */ | |||
| LilvScalePoints* | |||
| lilv_scale_points_new(void) | |||
| { | |||
| return lilv_collection_new(lilv_ptr_cmp, | |||
| (ZixDestroyFunc)lilv_scale_point_free); | |||
| } | |||
| LilvNodes* | |||
| lilv_nodes_new(void) | |||
| { | |||
| return lilv_collection_new(lilv_ptr_cmp, | |||
| (ZixDestroyFunc)lilv_node_free); | |||
| } | |||
| LilvUIs* | |||
| lilv_uis_new(void) | |||
| { | |||
| return lilv_collection_new(lilv_header_compare_by_uri, | |||
| (ZixDestroyFunc)lilv_ui_free); | |||
| } | |||
| LilvPluginClasses* | |||
| lilv_plugin_classes_new(void) | |||
| { | |||
| return lilv_collection_new(lilv_header_compare_by_uri, | |||
| (ZixDestroyFunc)lilv_plugin_class_free); | |||
| } | |||
| /* URI based accessors (for collections of things with URIs) */ | |||
| LILV_API | |||
| const LilvPluginClass* | |||
| lilv_plugin_classes_get_by_uri(const LilvPluginClasses* coll, | |||
| const LilvNode* uri) | |||
| { | |||
| return (LilvPluginClass*)lilv_collection_get_by_uri( | |||
| (const ZixTree*)coll, uri); | |||
| } | |||
| LILV_API | |||
| const LilvUI* | |||
| lilv_uis_get_by_uri(const LilvUIs* coll, const LilvNode* uri) | |||
| { | |||
| return (LilvUI*)lilv_collection_get_by_uri((const ZixTree*)coll, uri); | |||
| } | |||
| /* Plugins */ | |||
| LilvPlugins* | |||
| lilv_plugins_new(void) | |||
| { | |||
| return lilv_collection_new(lilv_header_compare_by_uri, NULL); | |||
| } | |||
| LILV_API | |||
| const LilvPlugin* | |||
| lilv_plugins_get_by_uri(const LilvPlugins* list, const LilvNode* uri) | |||
| { | |||
| return (LilvPlugin*)lilv_collection_get_by_uri((const ZixTree*)list, uri); | |||
| } | |||
| /* Nodes */ | |||
| LILV_API | |||
| bool | |||
| lilv_nodes_contains(const LilvNodes* list, const LilvNode* value) | |||
| { | |||
| LILV_FOREACH(nodes, i, list) | |||
| if (lilv_node_equals(lilv_nodes_get(list, i), value)) | |||
| return true; | |||
| return false; | |||
| } | |||
| /* Iterator */ | |||
| #define LILV_COLLECTION_IMPL(prefix, CT, ET) \ | |||
| LILV_API \ | |||
| unsigned \ | |||
| prefix##_size(const CT* collection) { \ | |||
| return lilv_collection_size(collection); \ | |||
| } \ | |||
| \ | |||
| LILV_API \ | |||
| LilvIter* \ | |||
| prefix##_begin(const CT* collection) { \ | |||
| return lilv_collection_begin(collection); \ | |||
| } \ | |||
| \ | |||
| LILV_API \ | |||
| const ET* \ | |||
| prefix##_get(const CT* collection, LilvIter* i) { \ | |||
| return (ET*)lilv_collection_get(collection, i); \ | |||
| } \ | |||
| \ | |||
| LILV_API \ | |||
| LilvIter* \ | |||
| prefix##_next(const CT* collection, LilvIter* i) { \ | |||
| return zix_tree_iter_next((ZixTreeIter*)i); \ | |||
| } \ | |||
| \ | |||
| LILV_API \ | |||
| bool \ | |||
| prefix##_is_end(const CT* collection, LilvIter* i) { \ | |||
| return zix_tree_iter_is_end((ZixTreeIter*)i); \ | |||
| } | |||
| LILV_COLLECTION_IMPL(lilv_plugin_classes, LilvPluginClasses, LilvPluginClass) | |||
| LILV_COLLECTION_IMPL(lilv_scale_points, LilvScalePoints, LilvScalePoint) | |||
| LILV_COLLECTION_IMPL(lilv_uis, LilvUIs, LilvUI) | |||
| LILV_COLLECTION_IMPL(lilv_nodes, LilvNodes, LilvNode) | |||
| LILV_COLLECTION_IMPL(lilv_plugins, LilvPlugins, LilvPlugin) | |||
| LILV_API | |||
| void | |||
| lilv_plugin_classes_free(LilvPluginClasses* collection) { | |||
| lilv_collection_free(collection); | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_scale_points_free(LilvScalePoints* collection) { | |||
| lilv_collection_free(collection); | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_uis_free(LilvUIs* collection) { | |||
| lilv_collection_free(collection); | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_nodes_free(LilvNodes* collection) { | |||
| lilv_collection_free(collection); | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_nodes_get_first(const LilvNodes* collection) { | |||
| return (LilvNode*)lilv_collection_get(collection, | |||
| lilv_collection_begin(collection)); | |||
| } | |||
| @@ -0,0 +1,126 @@ | |||
| /* | |||
| Copyright 2007-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lilv_internal.h" | |||
| LILV_API | |||
| LilvInstance* | |||
| lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
| double sample_rate, | |||
| const LV2_Feature*const* features) | |||
| { | |||
| lilv_plugin_load_if_necessary(plugin); | |||
| LilvInstance* result = NULL; | |||
| const LV2_Feature** local_features = NULL; | |||
| if (features == NULL) { | |||
| local_features = (const LV2_Feature**)malloc(sizeof(LV2_Feature)); | |||
| local_features[0] = NULL; | |||
| } | |||
| const LilvNode* const lib_uri = lilv_plugin_get_library_uri(plugin); | |||
| const LilvNode* const bundle_uri = lilv_plugin_get_bundle_uri(plugin); | |||
| const char* bundle_path = lilv_uri_to_path( | |||
| lilv_node_as_uri(lilv_plugin_get_bundle_uri(plugin))); | |||
| LilvLib* lib = lilv_lib_open(plugin->world, lib_uri, bundle_path, features); | |||
| if (!lib) { | |||
| return NULL; | |||
| } | |||
| // Parse bundle URI to use as base URI | |||
| const char* bundle_uri_str = lilv_node_as_uri(bundle_uri); | |||
| SerdURI base_uri; | |||
| if (serd_uri_parse((const uint8_t*)bundle_uri_str, &base_uri)) { | |||
| lilv_lib_close(lib); | |||
| return NULL; | |||
| } | |||
| // Search for plugin by URI | |||
| for (uint32_t i = 0; true; ++i) { | |||
| const LV2_Descriptor* ld = lilv_lib_get_plugin(lib, i); | |||
| if (!ld) { | |||
| LILV_ERRORF("No plugin <%s> in <%s>\n", | |||
| lilv_node_as_uri(lilv_plugin_get_uri(plugin)), | |||
| lilv_node_as_uri(lib_uri)); | |||
| lilv_lib_close(lib); | |||
| break; // return NULL | |||
| } | |||
| // Resolve library plugin URI against base URI | |||
| SerdURI abs_uri; | |||
| SerdNode abs_uri_node = serd_node_new_uri_from_string( | |||
| (const uint8_t*)ld->URI, &base_uri, &abs_uri); | |||
| if (!abs_uri_node.buf) { | |||
| LILV_ERRORF("Failed to parse plugin URI `%s'\n", ld->URI); | |||
| lilv_lib_close(lib); | |||
| break; | |||
| } | |||
| if (!strcmp((const char*)abs_uri_node.buf, | |||
| lilv_node_as_uri(lilv_plugin_get_uri(plugin)))) { | |||
| // Create LilvInstance to return | |||
| result = (LilvInstance*)malloc(sizeof(LilvInstance)); | |||
| result->lv2_descriptor = ld; | |||
| result->lv2_handle = ld->instantiate( | |||
| ld, sample_rate, bundle_path, | |||
| (features) ? features : local_features); | |||
| result->pimpl = lib; | |||
| serd_node_free(&abs_uri_node); | |||
| break; | |||
| } else { | |||
| serd_node_free(&abs_uri_node); | |||
| } | |||
| } | |||
| if (result) { | |||
| // Failed to instantiate | |||
| if (result->lv2_handle == NULL) { | |||
| free(result); | |||
| return NULL; | |||
| } | |||
| // "Connect" all ports to NULL (catches bugs) | |||
| for (uint32_t i = 0; i < lilv_plugin_get_num_ports(plugin); ++i) | |||
| result->lv2_descriptor->connect_port(result->lv2_handle, i, NULL); | |||
| } | |||
| free(local_features); | |||
| return result; | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_instance_free(LilvInstance* instance) | |||
| { | |||
| if (!instance) | |||
| return; | |||
| instance->lv2_descriptor->cleanup(instance->lv2_handle); | |||
| instance->lv2_descriptor = NULL; | |||
| lilv_lib_close((LilvLib*)instance->pimpl); | |||
| instance->pimpl = NULL; | |||
| free(instance); | |||
| } | |||
| @@ -0,0 +1,111 @@ | |||
| /* | |||
| Copyright 2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "lilv_internal.h" | |||
| LilvLib* | |||
| lilv_lib_open(LilvWorld* world, | |||
| const LilvNode* uri, | |||
| const char* bundle_path, | |||
| const LV2_Feature*const* features) | |||
| { | |||
| ZixTreeIter* i = NULL; | |||
| const struct LilvHeader key = { world, (LilvNode*)uri }; | |||
| if (!zix_tree_find(world->libs, &key, &i)) { | |||
| LilvLib* llib = (LilvLib*)zix_tree_get(i); | |||
| ++llib->refs; | |||
| return llib; | |||
| } | |||
| const char* const lib_uri = lilv_node_as_uri(uri); | |||
| const char* const lib_path = lilv_uri_to_path(lib_uri); | |||
| if (!lib_path) { | |||
| return NULL; | |||
| } | |||
| dlerror(); | |||
| void* lib = dlopen(lib_path, RTLD_NOW); | |||
| if (!lib) { | |||
| LILV_ERRORF("Failed to open library %s (%s)\n", lib_path, dlerror()); | |||
| return NULL; | |||
| } | |||
| LV2_Descriptor_Function df = (LV2_Descriptor_Function) | |||
| lilv_dlfunc(lib, "lv2_descriptor"); | |||
| #ifdef LILV_NEW_LV2 | |||
| LV2_Lib_Descriptor_Function ldf = (LV2_Lib_Descriptor_Function) | |||
| lilv_dlfunc(lib, "lv2_lib_descriptor"); | |||
| const LV2_Lib_Descriptor* desc = NULL; | |||
| if (ldf) { | |||
| desc = ldf(bundle_path, features); | |||
| if (!desc) { | |||
| LILV_ERRORF("Call to `lv2_lib_descriptor' in %s failed\n", lib_path); | |||
| return NULL; | |||
| } | |||
| } else | |||
| #endif | |||
| if (!df) { | |||
| LILV_ERRORF("No `lv2_descriptor' or `lv2_lib_descriptor' in %s\n", | |||
| lib_path); | |||
| dlclose(lib); | |||
| return NULL; | |||
| } | |||
| LilvLib* llib = (LilvLib*)malloc(sizeof(LilvLib)); | |||
| llib->world = world; | |||
| llib->uri = lilv_node_duplicate(uri); | |||
| llib->lib = lib; | |||
| llib->lv2_descriptor = df; | |||
| #ifdef LILV_NEW_LV2 | |||
| llib->desc = desc; | |||
| #endif | |||
| llib->refs = 1; | |||
| zix_tree_insert(world->libs, llib, NULL); | |||
| return llib; | |||
| } | |||
| const LV2_Descriptor* | |||
| lilv_lib_get_plugin(LilvLib* lib, uint32_t index) | |||
| { | |||
| if (lib->lv2_descriptor) { | |||
| return lib->lv2_descriptor(index); | |||
| } | |||
| #ifdef LILV_NEW_LV2 | |||
| if (lib->desc) { | |||
| return lib->desc->get_plugin(lib->desc->handle, index); | |||
| } | |||
| #endif | |||
| return NULL; | |||
| } | |||
| void | |||
| lilv_lib_close(LilvLib* lib) | |||
| { | |||
| if (--lib->refs == 0) { | |||
| dlclose(lib->lib); | |||
| ZixTreeIter* i = NULL; | |||
| if (lib->world->libs && !zix_tree_find(lib->world->libs, lib, &i)) { | |||
| zix_tree_remove(lib->world->libs, i); | |||
| } | |||
| lilv_node_free(lib->uri); | |||
| free(lib); | |||
| } | |||
| } | |||
| @@ -0,0 +1,382 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef LILV_INTERNAL_H | |||
| #define LILV_INTERNAL_H | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| #include <stddef.h> | |||
| #include <stdint.h> | |||
| #include <stdlib.h> | |||
| #include <float.h> | |||
| #ifdef _WIN32 | |||
| # include <windows.h> | |||
| # define dlopen(path, flags) LoadLibrary(path) | |||
| # define dlclose(lib) FreeLibrary((HMODULE)lib) | |||
| # define dlsym GetProcAddress | |||
| # ifdef _MSC_VER | |||
| # define __func__ __FUNCTION__ | |||
| # define INFINITY DBL_MAX + DBL_MAX | |||
| # define NAN INFINITY - INFINITY | |||
| # define snprintf _snprintf | |||
| # endif | |||
| static inline char* dlerror(void) { return "Unknown error"; } | |||
| #else | |||
| # include <dlfcn.h> | |||
| #endif | |||
| #include "serd/serd.h" | |||
| #include "sord/sord.h" | |||
| #include "zix/tree.h" | |||
| #include "lilv_config.h" | |||
| #include "lilv/lilv.h" | |||
| #ifdef LILV_DYN_MANIFEST | |||
| # include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h" | |||
| #endif | |||
| /* | |||
| * | |||
| * Types | |||
| * | |||
| */ | |||
| typedef struct LilvSpecImpl LilvSpec; | |||
| typedef void LilvCollection; | |||
| struct LilvPortImpl { | |||
| SordNode* node; ///< RDF node | |||
| uint32_t index; ///< lv2:index | |||
| LilvNode* symbol; ///< lv2:symbol | |||
| LilvNodes* classes; ///< rdf:type | |||
| }; | |||
| struct LilvSpecImpl { | |||
| SordNode* spec; | |||
| SordNode* bundle; | |||
| LilvNodes* data_uris; | |||
| struct LilvSpecImpl* next; | |||
| }; | |||
| /** | |||
| Header of an LilvPlugin, LilvPluginClass, or LilvUI. | |||
| Any of these structs may be safely casted to LilvHeader, which is used to | |||
| implement collections using the same comparator. | |||
| */ | |||
| struct LilvHeader { | |||
| LilvWorld* world; | |||
| LilvNode* uri; | |||
| }; | |||
| #ifdef LILV_DYN_MANIFEST | |||
| typedef struct { | |||
| LilvNode* bundle; | |||
| void* lib; | |||
| LV2_Dyn_Manifest_Handle handle; | |||
| uint32_t refs; | |||
| } LilvDynManifest; | |||
| #endif | |||
| typedef struct { | |||
| LilvWorld* world; | |||
| LilvNode* uri; | |||
| void* lib; | |||
| LV2_Descriptor_Function lv2_descriptor; | |||
| #ifdef LILV_NEW_LV2 | |||
| const LV2_Lib_Descriptor* desc; | |||
| #endif | |||
| uint32_t refs; | |||
| } LilvLib; | |||
| struct LilvPluginImpl { | |||
| LilvWorld* world; | |||
| LilvNode* plugin_uri; | |||
| LilvNode* bundle_uri; ///< Bundle plugin was loaded from | |||
| LilvNode* binary_uri; ///< lv2:binary | |||
| #ifdef LILV_DYN_MANIFEST | |||
| LilvDynManifest* dynmanifest; | |||
| #endif | |||
| const LilvPluginClass* plugin_class; | |||
| LilvNodes* data_uris; ///< rdfs::seeAlso | |||
| LilvPort** ports; | |||
| uint32_t num_ports; | |||
| bool loaded; | |||
| bool replaced; | |||
| }; | |||
| struct LilvPluginClassImpl { | |||
| LilvWorld* world; | |||
| LilvNode* uri; | |||
| LilvNode* parent_uri; | |||
| LilvNode* label; | |||
| }; | |||
| struct LilvInstancePimpl { | |||
| LilvWorld* world; | |||
| LilvLib* lib; | |||
| }; | |||
| typedef struct { | |||
| bool dyn_manifest; | |||
| bool filter_language; | |||
| } LilvOptions; | |||
| struct LilvWorldImpl { | |||
| SordWorld* world; | |||
| SordModel* model; | |||
| SerdReader* reader; | |||
| unsigned n_read_files; | |||
| LilvPluginClass* lv2_plugin_class; | |||
| LilvPluginClasses* plugin_classes; | |||
| LilvSpec* specs; | |||
| LilvPlugins* plugins; | |||
| LilvNodes* loaded_files; | |||
| ZixTree* libs; | |||
| struct { | |||
| SordNode* dc_replaces; | |||
| SordNode* dman_DynManifest; | |||
| SordNode* doap_name; | |||
| SordNode* lv2_Plugin; | |||
| SordNode* lv2_Specification; | |||
| SordNode* lv2_appliesTo; | |||
| SordNode* lv2_binary; | |||
| SordNode* lv2_default; | |||
| SordNode* lv2_designation; | |||
| SordNode* lv2_extensionData; | |||
| SordNode* lv2_index; | |||
| SordNode* lv2_maximum; | |||
| SordNode* lv2_minimum; | |||
| SordNode* lv2_name; | |||
| SordNode* lv2_optionalFeature; | |||
| SordNode* lv2_port; | |||
| SordNode* lv2_portProperty; | |||
| SordNode* lv2_reportsLatency; | |||
| SordNode* lv2_requiredFeature; | |||
| SordNode* lv2_symbol; | |||
| SordNode* null_uri; | |||
| SordNode* pset_value; | |||
| SordNode* rdf_a; | |||
| SordNode* rdf_value; | |||
| SordNode* rdfs_Class; | |||
| SordNode* rdfs_label; | |||
| SordNode* rdfs_seeAlso; | |||
| SordNode* rdfs_subClassOf; | |||
| SordNode* xsd_base64Binary; | |||
| SordNode* xsd_boolean; | |||
| SordNode* xsd_decimal; | |||
| SordNode* xsd_double; | |||
| SordNode* xsd_integer; | |||
| } uris; | |||
| LilvOptions opt; | |||
| }; | |||
| typedef enum { | |||
| LILV_VALUE_URI, | |||
| LILV_VALUE_STRING, | |||
| LILV_VALUE_INT, | |||
| LILV_VALUE_FLOAT, | |||
| LILV_VALUE_BOOL, | |||
| LILV_VALUE_BLANK, | |||
| LILV_VALUE_BLOB | |||
| } LilvNodeType; | |||
| struct LilvNodeImpl { | |||
| LilvWorld* world; | |||
| SordNode* node; | |||
| LilvNodeType type; | |||
| union { | |||
| int int_val; | |||
| float float_val; | |||
| bool bool_val; | |||
| } val; | |||
| }; | |||
| struct LilvScalePointImpl { | |||
| LilvNode* value; | |||
| LilvNode* label; | |||
| }; | |||
| struct LilvUIImpl { | |||
| LilvWorld* world; | |||
| LilvNode* uri; | |||
| LilvNode* bundle_uri; | |||
| LilvNode* binary_uri; | |||
| LilvNodes* classes; | |||
| }; | |||
| /* | |||
| * | |||
| * Functions | |||
| * | |||
| */ | |||
| LilvPort* lilv_port_new(LilvWorld* world, | |||
| const SordNode* node, | |||
| uint32_t index, | |||
| const char* symbol); | |||
| void lilv_port_free(const LilvPlugin* plugin, LilvPort* port); | |||
| LilvPlugin* lilv_plugin_new(LilvWorld* world, | |||
| LilvNode* uri, | |||
| LilvNode* bundle_uri); | |||
| void lilv_plugin_load_if_necessary(const LilvPlugin* p); | |||
| void lilv_plugin_free(LilvPlugin* plugin); | |||
| LilvNode* lilv_plugin_get_unique(const LilvPlugin* p, | |||
| const SordNode* subject, | |||
| const SordNode* predicate); | |||
| void lilv_collection_free(LilvCollection* collection); | |||
| unsigned lilv_collection_size(const LilvCollection* collection); | |||
| LilvIter* lilv_collection_begin(const LilvCollection* collection); | |||
| void* lilv_collection_get(const LilvCollection* collection, | |||
| const LilvIter* i); | |||
| LilvPluginClass* lilv_plugin_class_new(LilvWorld* world, | |||
| const SordNode* parent_uri, | |||
| const SordNode* uri, | |||
| const char* label); | |||
| void lilv_plugin_class_free(LilvPluginClass* plugin_class); | |||
| LilvLib* | |||
| lilv_lib_open(LilvWorld* world, | |||
| const LilvNode* uri, | |||
| const char* bundle_path, | |||
| const LV2_Feature*const* features); | |||
| const LV2_Descriptor* lilv_lib_get_plugin(LilvLib* lib, uint32_t index); | |||
| void lilv_lib_close(LilvLib* lib); | |||
| LilvNodes* lilv_nodes_new(void); | |||
| LilvPlugins* lilv_plugins_new(void); | |||
| LilvScalePoints* lilv_scale_points_new(void); | |||
| LilvPluginClasses* lilv_plugin_classes_new(void); | |||
| LilvUIs* lilv_uis_new(void); | |||
| const uint8_t* lilv_world_blank_node_prefix(LilvWorld* world); | |||
| void lilv_world_load_file(LilvWorld* world, const char* file_uri); | |||
| LilvUI* lilv_ui_new(LilvWorld* world, | |||
| LilvNode* uri, | |||
| LilvNode* type_uri, | |||
| LilvNode* binary_uri); | |||
| void lilv_ui_free(LilvUI* ui); | |||
| LilvNode* lilv_node_new(LilvWorld* world, | |||
| LilvNodeType type, | |||
| const char* val); | |||
| LilvNode* lilv_node_new_from_node(LilvWorld* world, | |||
| const SordNode* node); | |||
| const SordNode* lilv_node_as_node(const LilvNode* value); | |||
| int lilv_header_compare_by_uri(const void* a, const void* b, void* user_data); | |||
| int | |||
| lilv_ptr_cmp(const void* a, const void* b, void* user_data); | |||
| int | |||
| lilv_resource_node_cmp(const void* a, const void* b, void* user_data); | |||
| struct LilvHeader* | |||
| lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri); | |||
| LilvScalePoint* lilv_scale_point_new(LilvNode* value, LilvNode* label); | |||
| void lilv_scale_point_free(LilvScalePoint* point); | |||
| SordIter* | |||
| lilv_world_query_internal(LilvWorld* world, | |||
| const SordNode* subject, | |||
| const SordNode* predicate, | |||
| const SordNode* object); | |||
| LilvNodes* | |||
| lilv_world_query_values_internal(LilvWorld* world, | |||
| const SordNode* subject, | |||
| const SordNode* predicate, | |||
| const SordNode* object); | |||
| #define FOREACH_MATCH(iter) \ | |||
| for (; !sord_iter_end(iter); sord_iter_next(iter)) | |||
| LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* w, | |||
| SordIter* stream, | |||
| SordQuadIndex field); | |||
| char* lilv_strjoin(const char* first, ...); | |||
| char* lilv_strdup(const char* str); | |||
| char* lilv_get_lang(void); | |||
| char* lilv_expand(const char* path); | |||
| char* lilv_dirname(const char* path); | |||
| int lilv_copy_file(const char* src, const char* dst); | |||
| bool lilv_path_exists(const char* path, void* ignored); | |||
| char* lilv_path_absolute(const char* path); | |||
| bool lilv_path_is_absolute(const char* path); | |||
| char* lilv_get_latest_copy(const char* path, const char* copy_path); | |||
| char* lilv_path_relative_to(const char* path, const char* base); | |||
| bool lilv_path_is_child(const char* path, const char* dir); | |||
| int lilv_flock(FILE* file, bool lock); | |||
| char* lilv_realpath(const char* path); | |||
| int lilv_symlink(const char* oldpath, const char* newpath); | |||
| int lilv_mkdir_p(const char* path); | |||
| char* lilv_path_join(const char* a, const char* b); | |||
| bool lilv_file_equals(const char* a_path, const char* b_path); | |||
| char* | |||
| lilv_find_free_path(const char* in_path, | |||
| bool (*exists)(const char*, void*), void* user_data); | |||
| void | |||
| lilv_dir_for_each(const char* path, | |||
| void* data, | |||
| void (*f)(const char* path, const char* name, void* data)); | |||
| typedef void (*VoidFunc)(void); | |||
| /** dlsym wrapper to return a function pointer (without annoying warning) */ | |||
| static inline VoidFunc | |||
| lilv_dlfunc(void* handle, const char* symbol) | |||
| { | |||
| typedef VoidFunc (*VoidFuncGetter)(void*, const char*); | |||
| VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym; | |||
| return dlfunc(handle, symbol); | |||
| } | |||
| #ifdef LILV_DYN_MANIFEST | |||
| static const LV2_Feature* const dman_features = { NULL }; | |||
| #endif | |||
| #define LILV_ERROR(str) fprintf(stderr, "%s(): error: " str, \ | |||
| __func__) | |||
| #define LILV_ERRORF(fmt, ...) fprintf(stderr, "%s(): error: " fmt, \ | |||
| __func__, __VA_ARGS__) | |||
| #define LILV_WARN(str) fprintf(stderr, "%s(): warning: " str, \ | |||
| __func__) | |||
| #define LILV_WARNF(fmt, ...) fprintf(stderr, "%s(): warning: " fmt, \ | |||
| __func__, __VA_ARGS__) | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif /* LILV_INTERNAL_H */ | |||
| @@ -0,0 +1,396 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lilv_internal.h" | |||
| static void | |||
| lilv_node_set_numerics_from_string(LilvNode* val, size_t len) | |||
| { | |||
| const char* str = (const char*)sord_node_get_string(val->node); | |||
| char* endptr; | |||
| switch (val->type) { | |||
| case LILV_VALUE_URI: | |||
| case LILV_VALUE_BLANK: | |||
| case LILV_VALUE_STRING: | |||
| case LILV_VALUE_BLOB: | |||
| break; | |||
| case LILV_VALUE_INT: | |||
| val->val.int_val = strtol(str, &endptr, 10); | |||
| break; | |||
| case LILV_VALUE_FLOAT: | |||
| val->val.float_val = serd_strtod(str, &endptr); | |||
| break; | |||
| case LILV_VALUE_BOOL: | |||
| val->val.bool_val = !strcmp(str, "true"); | |||
| break; | |||
| } | |||
| } | |||
| /** Note that if @a type is numeric or boolean, the returned value is corrupt | |||
| * until lilv_node_set_numerics_from_string is called. It is not | |||
| * automatically called from here to avoid overhead and imprecision when the | |||
| * exact string value is known. | |||
| */ | |||
| LilvNode* | |||
| lilv_node_new(LilvWorld* world, LilvNodeType type, const char* str) | |||
| { | |||
| LilvNode* val = (LilvNode*)malloc(sizeof(LilvNode)); | |||
| val->world = world; | |||
| val->type = type; | |||
| const uint8_t* ustr = (const uint8_t*)str; | |||
| switch (type) { | |||
| case LILV_VALUE_URI: | |||
| val->node = sord_new_uri(world->world, ustr); | |||
| break; | |||
| case LILV_VALUE_BLANK: | |||
| val->node = sord_new_blank(world->world, ustr); | |||
| break; | |||
| case LILV_VALUE_STRING: | |||
| val->node = sord_new_literal(world->world, NULL, ustr, NULL); | |||
| break; | |||
| case LILV_VALUE_INT: | |||
| val->node = sord_new_literal( | |||
| world->world, world->uris.xsd_integer, ustr, NULL); | |||
| break; | |||
| case LILV_VALUE_FLOAT: | |||
| val->node = sord_new_literal( | |||
| world->world, world->uris.xsd_decimal, ustr, NULL); | |||
| break; | |||
| case LILV_VALUE_BOOL: | |||
| val->node = sord_new_literal( | |||
| world->world, world->uris.xsd_boolean, ustr, NULL); | |||
| break; | |||
| case LILV_VALUE_BLOB: | |||
| val->node = sord_new_literal( | |||
| world->world, world->uris.xsd_base64Binary, ustr, NULL); | |||
| break; | |||
| } | |||
| return val; | |||
| } | |||
| /** Create a new LilvNode from @a node, or return NULL if impossible */ | |||
| LilvNode* | |||
| lilv_node_new_from_node(LilvWorld* world, const SordNode* node) | |||
| { | |||
| LilvNode* result = NULL; | |||
| SordNode* datatype_uri = NULL; | |||
| LilvNodeType type = LILV_VALUE_STRING; | |||
| size_t len = 0; | |||
| switch (sord_node_get_type(node)) { | |||
| case SORD_URI: | |||
| result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
| result->world = (LilvWorld*)world; | |||
| result->type = LILV_VALUE_URI; | |||
| result->node = sord_node_copy(node); | |||
| break; | |||
| case SORD_BLANK: | |||
| result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
| result->world = (LilvWorld*)world; | |||
| result->type = LILV_VALUE_BLANK; | |||
| result->node = sord_node_copy(node); | |||
| break; | |||
| case SORD_LITERAL: | |||
| datatype_uri = sord_node_get_datatype(node); | |||
| if (datatype_uri) { | |||
| if (sord_node_equals(datatype_uri, world->uris.xsd_boolean)) | |||
| type = LILV_VALUE_BOOL; | |||
| else if (sord_node_equals(datatype_uri, world->uris.xsd_decimal) | |||
| || sord_node_equals(datatype_uri, world->uris.xsd_double)) | |||
| type = LILV_VALUE_FLOAT; | |||
| else if (sord_node_equals(datatype_uri, world->uris.xsd_integer)) | |||
| type = LILV_VALUE_INT; | |||
| else if (sord_node_equals(datatype_uri, | |||
| world->uris.xsd_base64Binary)) | |||
| type = LILV_VALUE_BLOB; | |||
| else | |||
| LILV_ERRORF("Unknown datatype `%s'\n", | |||
| sord_node_get_string(datatype_uri)); | |||
| } | |||
| result = lilv_node_new( | |||
| world, type, (const char*)sord_node_get_string_counted(node, &len)); | |||
| lilv_node_set_numerics_from_string(result, len); | |||
| break; | |||
| default: | |||
| assert(false); | |||
| } | |||
| return result; | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_new_uri(LilvWorld* world, const char* uri) | |||
| { | |||
| return lilv_node_new(world, LILV_VALUE_URI, uri); | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_new_string(LilvWorld* world, const char* str) | |||
| { | |||
| return lilv_node_new(world, LILV_VALUE_STRING, str); | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_new_int(LilvWorld* world, int val) | |||
| { | |||
| char str[32]; | |||
| snprintf(str, sizeof(str), "%d", val); | |||
| LilvNode* ret = lilv_node_new(world, LILV_VALUE_INT, str); | |||
| ret->val.int_val = val; | |||
| return ret; | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_new_float(LilvWorld* world, float val) | |||
| { | |||
| char str[32]; | |||
| snprintf(str, sizeof(str), "%f", val); | |||
| LilvNode* ret = lilv_node_new(world, LILV_VALUE_FLOAT, str); | |||
| ret->val.float_val = val; | |||
| return ret; | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_new_bool(LilvWorld* world, bool val) | |||
| { | |||
| LilvNode* ret = lilv_node_new(world, LILV_VALUE_BOOL, | |||
| val ? "true" : "false"); | |||
| ret->val.bool_val = val; | |||
| return ret; | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_node_duplicate(const LilvNode* val) | |||
| { | |||
| if (!val) { | |||
| return NULL; | |||
| } | |||
| LilvNode* result = (LilvNode*)malloc(sizeof(LilvNode)); | |||
| result->world = val->world; | |||
| result->node = sord_node_copy(val->node); | |||
| result->val = val->val; | |||
| result->type = val->type; | |||
| return result; | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_node_free(LilvNode* val) | |||
| { | |||
| if (val) { | |||
| sord_node_free(val->world->world, val->node); | |||
| free(val); | |||
| } | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_equals(const LilvNode* value, const LilvNode* other) | |||
| { | |||
| if (value == NULL && other == NULL) | |||
| return true; | |||
| else if (value == NULL || other == NULL) | |||
| return false; | |||
| else if (value->type != other->type) | |||
| return false; | |||
| switch (value->type) { | |||
| case LILV_VALUE_URI: | |||
| case LILV_VALUE_BLANK: | |||
| case LILV_VALUE_STRING: | |||
| case LILV_VALUE_BLOB: | |||
| return sord_node_equals(value->node, other->node); | |||
| case LILV_VALUE_INT: | |||
| return (value->val.int_val == other->val.int_val); | |||
| case LILV_VALUE_FLOAT: | |||
| return (value->val.float_val == other->val.float_val); | |||
| case LILV_VALUE_BOOL: | |||
| return (value->val.bool_val == other->val.bool_val); | |||
| } | |||
| return false; /* shouldn't get here */ | |||
| } | |||
| LILV_API | |||
| char* | |||
| lilv_node_get_turtle_token(const LilvNode* value) | |||
| { | |||
| const char* str = (const char*)sord_node_get_string(value->node); | |||
| size_t len = 0; | |||
| char* result = NULL; | |||
| SerdNode node; | |||
| switch (value->type) { | |||
| case LILV_VALUE_URI: | |||
| len = strlen(str) + 3; | |||
| result = (char*)calloc(len, 1); | |||
| snprintf(result, len, "<%s>", str); | |||
| break; | |||
| case LILV_VALUE_BLANK: | |||
| len = strlen(str) + 3; | |||
| result = (char*)calloc(len, 1); | |||
| snprintf(result, len, "_:%s", str); | |||
| break; | |||
| case LILV_VALUE_STRING: | |||
| case LILV_VALUE_BOOL: | |||
| case LILV_VALUE_BLOB: | |||
| result = lilv_strdup(str); | |||
| break; | |||
| case LILV_VALUE_INT: | |||
| node = serd_node_new_integer(value->val.int_val); | |||
| result = (char*)node.buf; | |||
| break; | |||
| case LILV_VALUE_FLOAT: | |||
| node = serd_node_new_decimal(value->val.float_val, 8); | |||
| result = (char*)node.buf; | |||
| break; | |||
| } | |||
| return result; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_uri(const LilvNode* value) | |||
| { | |||
| return (value && value->type == LILV_VALUE_URI); | |||
| } | |||
| LILV_API | |||
| const char* | |||
| lilv_node_as_uri(const LilvNode* value) | |||
| { | |||
| assert(lilv_node_is_uri(value)); | |||
| return (const char*)sord_node_get_string(value->node); | |||
| } | |||
| const SordNode* | |||
| lilv_node_as_node(const LilvNode* value) | |||
| { | |||
| assert(lilv_node_is_uri(value)); | |||
| return value->node; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_blank(const LilvNode* value) | |||
| { | |||
| return (value && value->type == LILV_VALUE_BLANK); | |||
| } | |||
| LILV_API | |||
| const char* | |||
| lilv_node_as_blank(const LilvNode* value) | |||
| { | |||
| assert(lilv_node_is_blank(value)); | |||
| return (const char*)sord_node_get_string(value->node); | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_literal(const LilvNode* value) | |||
| { | |||
| if (!value) | |||
| return false; | |||
| switch (value->type) { | |||
| case LILV_VALUE_STRING: | |||
| case LILV_VALUE_INT: | |||
| case LILV_VALUE_FLOAT: | |||
| return true; | |||
| default: | |||
| return false; | |||
| } | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_string(const LilvNode* value) | |||
| { | |||
| return (value && value->type == LILV_VALUE_STRING); | |||
| } | |||
| LILV_API | |||
| const char* | |||
| lilv_node_as_string(const LilvNode* value) | |||
| { | |||
| return value ? (const char*)sord_node_get_string(value->node) : NULL; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_int(const LilvNode* value) | |||
| { | |||
| return (value && value->type == LILV_VALUE_INT); | |||
| } | |||
| LILV_API | |||
| int | |||
| lilv_node_as_int(const LilvNode* value) | |||
| { | |||
| assert(value); | |||
| assert(lilv_node_is_int(value)); | |||
| return value->val.int_val; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_float(const LilvNode* value) | |||
| { | |||
| return (value && value->type == LILV_VALUE_FLOAT); | |||
| } | |||
| LILV_API | |||
| float | |||
| lilv_node_as_float(const LilvNode* value) | |||
| { | |||
| assert(lilv_node_is_float(value) || lilv_node_is_int(value)); | |||
| if (lilv_node_is_float(value)) { | |||
| return value->val.float_val; | |||
| } else { // lilv_node_is_int(value) | |||
| return (float)value->val.int_val; | |||
| } | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_is_bool(const LilvNode* value) | |||
| { | |||
| return (value && value->type == LILV_VALUE_BOOL); | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_node_as_bool(const LilvNode* value) | |||
| { | |||
| assert(value); | |||
| assert(lilv_node_is_bool(value)); | |||
| return value->val.bool_val; | |||
| } | |||
| @@ -0,0 +1,96 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lilv_internal.h" | |||
| LilvPluginClass* | |||
| lilv_plugin_class_new(LilvWorld* world, | |||
| const SordNode* parent_node, | |||
| const SordNode* uri, | |||
| const char* label) | |||
| { | |||
| if (parent_node && sord_node_get_type(parent_node) != SORD_URI) { | |||
| return NULL; // Not an LV2 plugin superclass (FIXME: discover properly) | |||
| } | |||
| LilvPluginClass* pc = (LilvPluginClass*)malloc(sizeof(LilvPluginClass)); | |||
| pc->world = world; | |||
| pc->uri = lilv_node_new_from_node(world, uri); | |||
| pc->label = lilv_node_new(world, LILV_VALUE_STRING, label); | |||
| pc->parent_uri = (parent_node) | |||
| ? lilv_node_new_from_node(world, parent_node) | |||
| : NULL; | |||
| return pc; | |||
| } | |||
| void | |||
| lilv_plugin_class_free(LilvPluginClass* plugin_class) | |||
| { | |||
| assert(plugin_class->uri); | |||
| lilv_node_free(plugin_class->uri); | |||
| lilv_node_free(plugin_class->parent_uri); | |||
| lilv_node_free(plugin_class->label); | |||
| free(plugin_class); | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_plugin_class_get_parent_uri(const LilvPluginClass* plugin_class) | |||
| { | |||
| if (plugin_class->parent_uri) | |||
| return plugin_class->parent_uri; | |||
| else | |||
| return NULL; | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class) | |||
| { | |||
| assert(plugin_class->uri); | |||
| return plugin_class->uri; | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_plugin_class_get_label(const LilvPluginClass* plugin_class) | |||
| { | |||
| return plugin_class->label; | |||
| } | |||
| LILV_API | |||
| LilvPluginClasses* | |||
| lilv_plugin_class_get_children(const LilvPluginClass* plugin_class) | |||
| { | |||
| // Returned list doesn't own categories | |||
| LilvPluginClasses* all = plugin_class->world->plugin_classes; | |||
| LilvPluginClasses* result = zix_tree_new(false, lilv_ptr_cmp, NULL, NULL); | |||
| for (ZixTreeIter* i = zix_tree_begin((ZixTree*)all); | |||
| i != zix_tree_end((ZixTree*)all); | |||
| i = zix_tree_iter_next(i)) { | |||
| const LilvPluginClass* c = (LilvPluginClass*)zix_tree_get(i); | |||
| const LilvNode* parent = lilv_plugin_class_get_parent_uri(c); | |||
| if (parent && lilv_node_equals(lilv_plugin_class_get_uri(plugin_class), | |||
| parent)) | |||
| zix_tree_insert((ZixTree*)result, (LilvPluginClass*)c, NULL); | |||
| } | |||
| return result; | |||
| } | |||
| @@ -0,0 +1,264 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <limits.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lv2/event.h" | |||
| #include "lilv_internal.h" | |||
| LilvPort* | |||
| lilv_port_new(LilvWorld* world, | |||
| const SordNode* node, | |||
| uint32_t index, | |||
| const char* symbol) | |||
| { | |||
| LilvPort* port = (LilvPort*)malloc(sizeof(LilvPort)); | |||
| port->node = sord_node_copy(node); | |||
| port->index = index; | |||
| port->symbol = lilv_node_new(world, LILV_VALUE_STRING, symbol); | |||
| port->classes = lilv_nodes_new(); | |||
| return port; | |||
| } | |||
| void | |||
| lilv_port_free(const LilvPlugin* plugin, LilvPort* port) | |||
| { | |||
| if (port) { | |||
| sord_node_free(plugin->world->world, port->node); | |||
| lilv_nodes_free(port->classes); | |||
| lilv_node_free(port->symbol); | |||
| free(port); | |||
| } | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_port_is_a(const LilvPlugin* plugin, | |||
| const LilvPort* port, | |||
| const LilvNode* port_class) | |||
| { | |||
| LILV_FOREACH(nodes, i, port->classes) | |||
| if (lilv_node_equals(lilv_nodes_get(port->classes, i), port_class)) | |||
| return true; | |||
| return false; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_port_has_property(const LilvPlugin* p, | |||
| const LilvPort* port, | |||
| const LilvNode* property) | |||
| { | |||
| assert(property); | |||
| SordIter* results = lilv_world_query_internal( | |||
| p->world, | |||
| port->node, | |||
| p->world->uris.lv2_portProperty, | |||
| lilv_node_as_node(property)); | |||
| const bool ret = !sord_iter_end(results); | |||
| sord_iter_free(results); | |||
| return ret; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_port_supports_event(const LilvPlugin* p, | |||
| const LilvPort* port, | |||
| const LilvNode* event) | |||
| { | |||
| assert(event); | |||
| SordIter* results = lilv_world_query_internal( | |||
| p->world, | |||
| port->node, | |||
| sord_new_uri(p->world->world, (const uint8_t*)LV2_EVENT__supportsEvent), | |||
| lilv_node_as_node(event)); | |||
| const bool ret = !sord_iter_end(results); | |||
| sord_iter_free(results); | |||
| return ret; | |||
| } | |||
| static LilvNodes* | |||
| lilv_port_get_value_by_node(const LilvPlugin* p, | |||
| const LilvPort* port, | |||
| const SordNode* predicate) | |||
| { | |||
| assert(sord_node_get_type(predicate) == SORD_URI); | |||
| SordIter* results = lilv_world_query_internal( | |||
| p->world, | |||
| port->node, | |||
| predicate, | |||
| NULL); | |||
| return lilv_nodes_from_stream_objects(p->world, results, SORD_OBJECT); | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_port_get_value(const LilvPlugin* p, | |||
| const LilvPort* port, | |||
| const LilvNode* predicate) | |||
| { | |||
| if (!lilv_node_is_uri(predicate)) { | |||
| LILV_ERRORF("Predicate `%s' is not a URI\n", | |||
| sord_node_get_string(predicate->node)); | |||
| return NULL; | |||
| } | |||
| return lilv_port_get_value_by_node( | |||
| p, port, | |||
| lilv_node_as_node(predicate)); | |||
| } | |||
| LILV_API | |||
| uint32_t | |||
| lilv_port_get_index(const LilvPlugin* p, | |||
| const LilvPort* port) | |||
| { | |||
| return port->index; | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_port_get_symbol(const LilvPlugin* p, | |||
| const LilvPort* port) | |||
| { | |||
| return port->symbol; | |||
| } | |||
| LILV_API | |||
| LilvNode* | |||
| lilv_port_get_name(const LilvPlugin* p, | |||
| const LilvPort* port) | |||
| { | |||
| LilvNodes* results = lilv_port_get_value_by_node( | |||
| p, port, p->world->uris.lv2_name); | |||
| LilvNode* ret = NULL; | |||
| if (results) { | |||
| LilvNode* val = lilv_nodes_get_first(results); | |||
| if (lilv_node_is_string(val)) | |||
| ret = lilv_node_duplicate(val); | |||
| lilv_nodes_free(results); | |||
| } | |||
| if (!ret) | |||
| LILV_WARNF("Plugin <%s> port has no (mandatory) doap:name\n", | |||
| lilv_node_as_string(lilv_plugin_get_uri(p))); | |||
| return ret; | |||
| } | |||
| LILV_API | |||
| const LilvNodes* | |||
| lilv_port_get_classes(const LilvPlugin* p, | |||
| const LilvPort* port) | |||
| { | |||
| return port->classes; | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_port_get_range(const LilvPlugin* p, | |||
| const LilvPort* port, | |||
| LilvNode** def, | |||
| LilvNode** min, | |||
| LilvNode** max) | |||
| { | |||
| if (def) { | |||
| LilvNodes* defaults = lilv_port_get_value_by_node( | |||
| p, port, p->world->uris.lv2_default); | |||
| *def = defaults | |||
| ? lilv_node_duplicate(lilv_nodes_get_first(defaults)) | |||
| : NULL; | |||
| lilv_nodes_free(defaults); | |||
| } | |||
| if (min) { | |||
| LilvNodes* minimums = lilv_port_get_value_by_node( | |||
| p, port, p->world->uris.lv2_minimum); | |||
| *min = minimums | |||
| ? lilv_node_duplicate(lilv_nodes_get_first(minimums)) | |||
| : NULL; | |||
| lilv_nodes_free(minimums); | |||
| } | |||
| if (max) { | |||
| LilvNodes* maximums = lilv_port_get_value_by_node( | |||
| p, port, p->world->uris.lv2_maximum); | |||
| *max = maximums | |||
| ? lilv_node_duplicate(lilv_nodes_get_first(maximums)) | |||
| : NULL; | |||
| lilv_nodes_free(maximums); | |||
| } | |||
| } | |||
| LILV_API | |||
| LilvScalePoints* | |||
| lilv_port_get_scale_points(const LilvPlugin* p, | |||
| const LilvPort* port) | |||
| { | |||
| SordIter* points = lilv_world_query_internal( | |||
| p->world, | |||
| port->node, | |||
| sord_new_uri(p->world->world, (const uint8_t*)LV2_CORE__scalePoint), | |||
| NULL); | |||
| LilvScalePoints* ret = NULL; | |||
| if (!sord_iter_end(points)) | |||
| ret = lilv_scale_points_new(); | |||
| FOREACH_MATCH(points) { | |||
| const SordNode* point = sord_iter_get_node(points, SORD_OBJECT); | |||
| LilvNode* value = lilv_plugin_get_unique( | |||
| p, | |||
| point, | |||
| p->world->uris.rdf_value); | |||
| LilvNode* label = lilv_plugin_get_unique( | |||
| p, | |||
| point, | |||
| p->world->uris.rdfs_label); | |||
| if (value && label) { | |||
| zix_tree_insert( | |||
| (ZixTree*)ret, lilv_scale_point_new(value, label), NULL); | |||
| } | |||
| } | |||
| sord_iter_free(points); | |||
| assert(!ret || lilv_nodes_size(ret) > 0); | |||
| return ret; | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_port_get_properties(const LilvPlugin* p, | |||
| const LilvPort* port) | |||
| { | |||
| LilvNode* pred = lilv_node_new_from_node( | |||
| p->world, p->world->uris.lv2_portProperty); | |||
| LilvNodes* ret = lilv_port_get_value(p, port, pred); | |||
| lilv_node_free(pred); | |||
| return ret; | |||
| } | |||
| @@ -0,0 +1,140 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <limits.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lilv_internal.h" | |||
| typedef enum { | |||
| LILV_LANG_MATCH_NONE, ///< Language does not match at all | |||
| LILV_LANG_MATCH_PARTIAL, ///< Partial (language, but not country) match | |||
| LILV_LANG_MATCH_EXACT ///< Exact (language and country) match | |||
| } LilvLangMatch; | |||
| static LilvLangMatch | |||
| lilv_lang_matches(const char* a, const char* b) | |||
| { | |||
| if (!strcmp(a, b)) { | |||
| return LILV_LANG_MATCH_EXACT; | |||
| } | |||
| const char* a_dash = strchr(a, '-'); | |||
| const size_t a_lang_len = a_dash ? (size_t)(a_dash - a) : strlen(a); | |||
| const char* b_dash = strchr(b, '-'); | |||
| const size_t b_lang_len = b_dash ? (size_t)(b_dash - b) : strlen(b); | |||
| if (a_lang_len == b_lang_len && !strncmp(a, b, a_lang_len)) { | |||
| return LILV_LANG_MATCH_PARTIAL; | |||
| } | |||
| return LILV_LANG_MATCH_NONE; | |||
| } | |||
| static LilvNodes* | |||
| lilv_nodes_from_stream_objects_i18n(LilvWorld* world, | |||
| SordIter* stream, | |||
| SordQuadIndex field) | |||
| { | |||
| LilvNodes* values = lilv_nodes_new(); | |||
| const SordNode* nolang = NULL; // Untranslated value | |||
| const SordNode* partial = NULL; // Partial language match | |||
| char* syslang = lilv_get_lang(); | |||
| FOREACH_MATCH(stream) { | |||
| const SordNode* value = sord_iter_get_node(stream, field); | |||
| if (sord_node_get_type(value) == SORD_LITERAL) { | |||
| const char* lang = sord_node_get_language(value); | |||
| LilvLangMatch lm = LILV_LANG_MATCH_NONE; | |||
| if (lang) { | |||
| lm = (syslang) | |||
| ? lilv_lang_matches(lang, syslang) | |||
| : LILV_LANG_MATCH_PARTIAL; | |||
| } else { | |||
| nolang = value; | |||
| if (!syslang) { | |||
| lm = LILV_LANG_MATCH_EXACT; | |||
| } | |||
| } | |||
| if (lm == LILV_LANG_MATCH_EXACT) { | |||
| // Exact language match, add to results | |||
| zix_tree_insert((ZixTree*)values, | |||
| lilv_node_new_from_node(world, value), | |||
| NULL); | |||
| } else if (lm == LILV_LANG_MATCH_PARTIAL) { | |||
| // Partial language match, save in case we find no exact | |||
| partial = value; | |||
| } | |||
| } else { | |||
| zix_tree_insert((ZixTree*)values, | |||
| lilv_node_new_from_node(world, value), | |||
| NULL); | |||
| } | |||
| } | |||
| sord_iter_free(stream); | |||
| free(syslang); | |||
| if (lilv_nodes_size(values) > 0) { | |||
| return values; | |||
| } | |||
| const SordNode* best = nolang; | |||
| if (syslang && partial) { | |||
| // Partial language match for system language | |||
| best = partial; | |||
| } else if (!best) { | |||
| // No languages matches at all, and no untranslated value | |||
| // Use any value, if possible | |||
| best = partial; | |||
| } | |||
| if (best) { | |||
| zix_tree_insert( | |||
| (ZixTree*)values, lilv_node_new_from_node(world, best), NULL); | |||
| } else { | |||
| // No matches whatsoever | |||
| lilv_nodes_free(values); | |||
| values = NULL; | |||
| } | |||
| return values; | |||
| } | |||
| LilvNodes* | |||
| lilv_nodes_from_stream_objects(LilvWorld* world, | |||
| SordIter* stream, | |||
| SordQuadIndex field) | |||
| { | |||
| if (sord_iter_end(stream)) { | |||
| sord_iter_free(stream); | |||
| return NULL; | |||
| } else if (world->opt.filter_language) { | |||
| return lilv_nodes_from_stream_objects_i18n(world, stream, field); | |||
| } else { | |||
| LilvNodes* values = lilv_nodes_new(); | |||
| FOREACH_MATCH(stream) { | |||
| const SordNode* value = sord_iter_get_node(stream, field); | |||
| LilvNode* node = lilv_node_new_from_node(world, value); | |||
| if (node) { | |||
| zix_tree_insert((ZixTree*)values, node, NULL); | |||
| } | |||
| } | |||
| sord_iter_free(stream); | |||
| return values; | |||
| } | |||
| } | |||
| @@ -0,0 +1,52 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "lilv_internal.h" | |||
| /** Ownership of value and label is taken */ | |||
| LilvScalePoint* | |||
| lilv_scale_point_new(LilvNode* value, LilvNode* label) | |||
| { | |||
| LilvScalePoint* point = (LilvScalePoint*)malloc( | |||
| sizeof(struct LilvScalePointImpl)); | |||
| point->value = value; | |||
| point->label = label; | |||
| return point; | |||
| } | |||
| void | |||
| lilv_scale_point_free(LilvScalePoint* point) | |||
| { | |||
| if (point) { | |||
| lilv_node_free(point->value); | |||
| lilv_node_free(point->label); | |||
| free(point); | |||
| } | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_scale_point_get_value(const LilvScalePoint* p) | |||
| { | |||
| return p->value; | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_scale_point_get_label(const LilvScalePoint* p) | |||
| { | |||
| return p->label; | |||
| } | |||
| @@ -0,0 +1,184 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lilv_internal.h" | |||
| LilvUI* | |||
| lilv_ui_new(LilvWorld* world, | |||
| LilvNode* uri, | |||
| LilvNode* type_uri, | |||
| LilvNode* binary_uri) | |||
| { | |||
| assert(uri); | |||
| assert(type_uri); | |||
| assert(binary_uri); | |||
| LilvUI* ui = (LilvUI*)malloc(sizeof(LilvUI)); | |||
| ui->world = world; | |||
| ui->uri = uri; | |||
| ui->binary_uri = binary_uri; | |||
| // FIXME: kludge | |||
| char* bundle = lilv_strdup(lilv_node_as_string(ui->binary_uri)); | |||
| char* last_slash = strrchr(bundle, '/') + 1; | |||
| *last_slash = '\0'; | |||
| ui->bundle_uri = lilv_new_uri(world, bundle); | |||
| free(bundle); | |||
| ui->classes = lilv_nodes_new(); | |||
| zix_tree_insert((ZixTree*)ui->classes, type_uri, NULL); | |||
| return ui; | |||
| } | |||
| void | |||
| lilv_ui_free(LilvUI* ui) | |||
| { | |||
| lilv_node_free(ui->uri); | |||
| ui->uri = NULL; | |||
| lilv_node_free(ui->bundle_uri); | |||
| ui->bundle_uri = NULL; | |||
| lilv_node_free(ui->binary_uri); | |||
| ui->binary_uri = NULL; | |||
| lilv_nodes_free(ui->classes); | |||
| free(ui); | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_ui_get_uri(const LilvUI* ui) | |||
| { | |||
| assert(ui); | |||
| assert(ui->uri); | |||
| return ui->uri; | |||
| } | |||
| LILV_API | |||
| unsigned | |||
| lilv_ui_is_supported(const LilvUI* ui, | |||
| LilvUISupportedFunc supported_func, | |||
| const LilvNode* container_type, | |||
| const LilvNode** ui_type) | |||
| { | |||
| const LilvNodes* classes = lilv_ui_get_classes(ui); | |||
| LILV_FOREACH(nodes, c, classes) { | |||
| const LilvNode* type = lilv_nodes_get(classes, c); | |||
| const unsigned q = supported_func(lilv_node_as_uri(container_type), | |||
| lilv_node_as_uri(type)); | |||
| if (q) { | |||
| if (ui_type) { | |||
| *ui_type = type; | |||
| } | |||
| return q; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| LILV_API | |||
| const LilvNodes* | |||
| lilv_ui_get_classes(const LilvUI* ui) | |||
| { | |||
| return ui->classes; | |||
| } | |||
| LILV_API | |||
| bool | |||
| lilv_ui_is_a(const LilvUI* ui, const LilvNode* ui_class_uri) | |||
| { | |||
| return lilv_nodes_contains(ui->classes, ui_class_uri); | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_ui_get_bundle_uri(const LilvUI* ui) | |||
| { | |||
| assert(ui); | |||
| assert(ui->bundle_uri); | |||
| return ui->bundle_uri; | |||
| } | |||
| LILV_API | |||
| const LilvNode* | |||
| lilv_ui_get_binary_uri(const LilvUI* ui) | |||
| { | |||
| assert(ui); | |||
| assert(ui->binary_uri); | |||
| return ui->binary_uri; | |||
| } | |||
| static LilvNodes* | |||
| lilv_ui_get_value_internal(const LilvUI* ui, | |||
| const SordNode* predicate) | |||
| { | |||
| return lilv_world_query_values_internal( | |||
| ui->world, ui->uri->node, predicate, NULL); | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_ui_get_supported_features(const LilvUI* ui) | |||
| { | |||
| LilvNodes* optional = lilv_ui_get_optional_features(ui); | |||
| LilvNodes* required = lilv_ui_get_required_features(ui); | |||
| LilvNodes* result = lilv_nodes_new(); | |||
| LILV_FOREACH(nodes, i, optional) | |||
| zix_tree_insert((ZixTree*)result, | |||
| lilv_node_duplicate(lilv_nodes_get(optional, i)), | |||
| NULL); | |||
| LILV_FOREACH(nodes, i, required) | |||
| zix_tree_insert((ZixTree*)result, | |||
| lilv_node_duplicate(lilv_nodes_get(required, i)), | |||
| NULL); | |||
| lilv_nodes_free(optional); | |||
| lilv_nodes_free(required); | |||
| return result; | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_ui_get_required_features(const LilvUI* ui) | |||
| { | |||
| return lilv_ui_get_value_internal( | |||
| ui, ui->world->uris.lv2_requiredFeature); | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_ui_get_optional_features(const LilvUI* ui) | |||
| { | |||
| return lilv_ui_get_value_internal( | |||
| ui, ui->world->uris.lv2_optionalFeature); | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_ui_get_extension_data(const LilvUI* ui) | |||
| { | |||
| return lilv_ui_get_value_internal(ui, ui->world->uris.lv2_extensionData); | |||
| } | |||
| @@ -0,0 +1,604 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #define _POSIX_C_SOURCE 1 /* for fileno */ | |||
| #define _BSD_SOURCE 1 /* for realpath, symlink */ | |||
| #ifdef __APPLE__ | |||
| # define _DARWIN_C_SOURCE 1 /* for flock */ | |||
| #endif | |||
| #include <assert.h> | |||
| #include <ctype.h> | |||
| #include <errno.h> | |||
| #include <stdarg.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #ifdef _WIN32 | |||
| # include <windows.h> | |||
| # include <direct.h> | |||
| # include <io.h> | |||
| # define F_OK 0 | |||
| # define mkdir(path, flags) _mkdir(path) | |||
| #else | |||
| # include <dirent.h> | |||
| # include <unistd.h> | |||
| #endif | |||
| #include <sys/stat.h> | |||
| #include <sys/types.h> | |||
| #include "lilv_internal.h" | |||
| #if defined(HAVE_FLOCK) && defined(HAVE_FILENO) | |||
| # include <sys/file.h> | |||
| #endif | |||
| #ifndef PAGE_SIZE | |||
| # define PAGE_SIZE 4096 | |||
| #endif | |||
| char* | |||
| lilv_strjoin(const char* first, ...) | |||
| { | |||
| size_t len = strlen(first); | |||
| char* result = (char*)malloc(len + 1); | |||
| memcpy(result, first, len); | |||
| va_list args; | |||
| va_start(args, first); | |||
| while (1) { | |||
| const char* const s = va_arg(args, const char *); | |||
| if (s == NULL) | |||
| break; | |||
| const size_t this_len = strlen(s); | |||
| if (!(result = (char*)realloc(result, len + this_len + 1))) { | |||
| free(result); | |||
| LILV_ERROR("realloc() failed\n"); | |||
| return NULL; | |||
| } | |||
| memcpy(result + len, s, this_len); | |||
| len += this_len; | |||
| } | |||
| va_end(args); | |||
| result[len] = '\0'; | |||
| return result; | |||
| } | |||
| char* | |||
| lilv_strdup(const char* str) | |||
| { | |||
| if (!str) { | |||
| return NULL; | |||
| } | |||
| const size_t len = strlen(str); | |||
| char* dup = (char*)malloc(len + 1); | |||
| memcpy(dup, str, len + 1); | |||
| return dup; | |||
| } | |||
| const char* | |||
| lilv_uri_to_path(const char* uri) | |||
| { | |||
| return (const char*)serd_uri_to_path((const uint8_t*)uri); | |||
| } | |||
| /** Return the current LANG converted to Turtle (i.e. RFC3066) style. | |||
| * For example, if LANG is set to "en_CA.utf-8", this returns "en-ca". | |||
| */ | |||
| char* | |||
| lilv_get_lang(void) | |||
| { | |||
| const char* const env_lang = getenv("LANG"); | |||
| if (!env_lang || !strcmp(env_lang, "") | |||
| || !strcmp(env_lang, "C") || !strcmp(env_lang, "POSIX")) { | |||
| return NULL; | |||
| } | |||
| const size_t env_lang_len = strlen(env_lang); | |||
| char* const lang = (char*)malloc(env_lang_len + 1); | |||
| for (size_t i = 0; i < env_lang_len + 1; ++i) { | |||
| if (env_lang[i] == '_') { | |||
| lang[i] = '-'; // Convert _ to - | |||
| } else if (env_lang[i] >= 'A' && env_lang[i] <= 'Z') { | |||
| lang[i] = env_lang[i] + ('a' - 'A'); // Convert to lowercase | |||
| } else if (env_lang[i] >= 'a' && env_lang[i] <= 'z') { | |||
| lang[i] = env_lang[i]; // Lowercase letter, copy verbatim | |||
| } else if (env_lang[i] >= '0' && env_lang[i] <= '9') { | |||
| lang[i] = env_lang[i]; // Digit, copy verbatim | |||
| } else if (env_lang[i] == '\0' || env_lang[i] == '.') { | |||
| // End, or start of suffix (e.g. en_CA.utf-8), finished | |||
| lang[i] = '\0'; | |||
| break; | |||
| } else { | |||
| LILV_ERRORF("Illegal LANG `%s' ignored\n", env_lang); | |||
| free(lang); | |||
| return NULL; | |||
| } | |||
| } | |||
| return lang; | |||
| } | |||
| /** Append suffix to dst, update dst_len, and return the realloc'd result. */ | |||
| static char* | |||
| strappend(char* dst, size_t* dst_len, const char* suffix, size_t suffix_len) | |||
| { | |||
| dst = (char*)realloc(dst, *dst_len + suffix_len + 1); | |||
| memcpy(dst + *dst_len, suffix, suffix_len); | |||
| dst[(*dst_len += suffix_len)] = '\0'; | |||
| return dst; | |||
| } | |||
| /** Append the value of the environment variable var to dst. */ | |||
| static char* | |||
| append_var(char* dst, size_t* dst_len, const char* var) | |||
| { | |||
| // Get value from environment | |||
| const char* val = getenv(var); | |||
| if (val) { // Value found, append it | |||
| return strappend(dst, dst_len, val, strlen(val)); | |||
| } else { // No value found, append variable reference as-is | |||
| return strappend(strappend(dst, dst_len, "$", 1), | |||
| dst_len, var, strlen(var)); | |||
| } | |||
| } | |||
| /** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */ | |||
| char* | |||
| lilv_expand(const char* path) | |||
| { | |||
| #ifdef _WIN32 | |||
| char* out = (char*)malloc(MAX_PATH); | |||
| ExpandEnvironmentStrings(path, out, MAX_PATH); | |||
| #else | |||
| char* out = NULL; | |||
| size_t len = 0; | |||
| const char* start = path; // Start of current chunk to copy | |||
| for (const char* s = path; *s;) { | |||
| if (*s == '$') { | |||
| // Hit $ (variable reference, e.g. $VAR_NAME) | |||
| for (const char* t = s + 1; ; ++t) { | |||
| if (!*t || (!isupper(*t) && !isdigit(*t) && *t != '_')) { | |||
| // Append preceding chunk | |||
| out = strappend(out, &len, start, s - start); | |||
| // Append variable value (or $VAR_NAME if not found) | |||
| char* var = (char*)calloc(t - s, 1); | |||
| memcpy(var, s + 1, t - s - 1); | |||
| out = append_var(out, &len, var); | |||
| free(var); | |||
| // Continue after variable reference | |||
| start = s = t; | |||
| break; | |||
| } | |||
| } | |||
| } else if (*s == '~' && (*(s + 1) == '/' || !*(s + 1))) { | |||
| // Hit ~ before slash or end of string (home directory reference) | |||
| out = strappend(out, &len, start, s - start); | |||
| out = append_var(out, &len, "HOME"); | |||
| start = ++s; | |||
| } else { | |||
| ++s; | |||
| } | |||
| } | |||
| if (*start) { | |||
| out = strappend(out, &len, start, strlen(start)); | |||
| } | |||
| #endif | |||
| return out; | |||
| } | |||
| static bool | |||
| lilv_is_dir_sep(const char c) | |||
| { | |||
| return c == '/' || c == LILV_DIR_SEP[0]; | |||
| } | |||
| char* | |||
| lilv_dirname(const char* path) | |||
| { | |||
| const char* s = path + strlen(path) - 1; // Last character | |||
| for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash | |||
| for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash | |||
| for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates | |||
| if (s == path) { // Hit beginning | |||
| return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); | |||
| } else { // Pointing to the last character of the result (inclusive) | |||
| char* dirname = (char*)malloc(s - path + 2); | |||
| memcpy(dirname, path, s - path + 1); | |||
| dirname[s - path + 1] = '\0'; | |||
| return dirname; | |||
| } | |||
| } | |||
| bool | |||
| lilv_path_exists(const char* path, void* ignored) | |||
| { | |||
| return !access(path, F_OK); | |||
| } | |||
| char* | |||
| lilv_find_free_path(const char* in_path, | |||
| bool (*exists)(const char*, void*), void* user_data) | |||
| { | |||
| const size_t in_path_len = strlen(in_path); | |||
| char* path = (char*)malloc(in_path_len + 7); | |||
| memcpy(path, in_path, in_path_len + 1); | |||
| for (int i = 2; i < 1000000; ++i) { | |||
| if (!exists(path, user_data)) { | |||
| return path; | |||
| } | |||
| snprintf(path, in_path_len + 7, "%s.%u", in_path, i); | |||
| } | |||
| return NULL; | |||
| } | |||
| int | |||
| lilv_copy_file(const char* src, const char* dst) | |||
| { | |||
| FILE* in = fopen(src, "r"); | |||
| if (!in) { | |||
| LILV_ERRORF("error opening %s (%s)\n", src, strerror(errno)); | |||
| return 1; | |||
| } | |||
| FILE* out = fopen(dst, "w"); | |||
| if (!out) { | |||
| LILV_ERRORF("error opening %s (%s)\n", dst, strerror(errno)); | |||
| fclose(in); | |||
| return 2; | |||
| } | |||
| char* page = (char*)malloc(PAGE_SIZE); | |||
| size_t n_read = 0; | |||
| while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) { | |||
| if (fwrite(page, 1, n_read, out) != n_read) { | |||
| LILV_ERRORF("write to %s failed (%s)\n", dst, strerror(errno)); | |||
| break; | |||
| } | |||
| } | |||
| const int ret = ferror(in) || ferror(out); | |||
| if (ferror(in)) { | |||
| LILV_ERRORF("read from %s failed (%s)\n", src, strerror(errno)); | |||
| } | |||
| free(page); | |||
| fclose(in); | |||
| fclose(out); | |||
| return ret; | |||
| } | |||
| bool | |||
| lilv_path_is_absolute(const char* path) | |||
| { | |||
| if (lilv_is_dir_sep(path[0])) { | |||
| return true; | |||
| } | |||
| #ifdef _WIN32 | |||
| if (isalpha(path[0]) && path[1] == ':' && lilv_is_dir_sep(path[2])) { | |||
| return true; | |||
| } | |||
| #endif | |||
| return false; | |||
| } | |||
| char* | |||
| lilv_path_absolute(const char* path) | |||
| { | |||
| if (lilv_path_is_absolute(path)) { | |||
| return lilv_strdup(path); | |||
| } else { | |||
| char* cwd = getcwd(NULL, 0); | |||
| char* abs_path = lilv_path_join(cwd, path); | |||
| free(cwd); | |||
| return abs_path; | |||
| } | |||
| } | |||
| char* | |||
| lilv_path_join(const char* a, const char* b) | |||
| { | |||
| if (!a) { | |||
| return lilv_strdup(b); | |||
| } | |||
| const size_t a_len = strlen(a); | |||
| const size_t b_len = b ? strlen(b) : 0; | |||
| const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0); | |||
| char* path = (char*)calloc(1, a_len + b_len + 2); | |||
| memcpy(path, a, pre_len); | |||
| path[pre_len] = '/'; | |||
| if (b) { | |||
| memcpy(path + pre_len + 1, | |||
| b + (lilv_is_dir_sep(b[0]) ? 1 : 0), | |||
| lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len); | |||
| } | |||
| return path; | |||
| } | |||
| static void | |||
| lilv_size_mtime(const char* path, off_t* size, time_t* time) | |||
| { | |||
| struct stat buf; | |||
| if (stat(path, &buf)) { | |||
| LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
| } | |||
| if (size) { | |||
| *size = buf.st_size; | |||
| } | |||
| if (time) { | |||
| *time = buf.st_mtime; | |||
| } | |||
| } | |||
| typedef struct { | |||
| char* pattern; | |||
| off_t orig_size; | |||
| time_t time; | |||
| char* latest; | |||
| } Latest; | |||
| static void | |||
| update_latest(const char* path, const char* name, void* data) | |||
| { | |||
| Latest* latest = (Latest*)data; | |||
| char* entry_path = lilv_path_join(path, name); | |||
| unsigned num; | |||
| if (sscanf(entry_path, latest->pattern, &num) == 1) { | |||
| off_t entry_size = 0; | |||
| time_t entry_time = 0; | |||
| lilv_size_mtime(entry_path, &entry_size, &entry_time); | |||
| if (entry_size == latest->orig_size && entry_time >= latest->time) { | |||
| free(latest->latest); | |||
| latest->latest = entry_path; | |||
| } | |||
| } | |||
| if (entry_path != latest->latest) { | |||
| free(entry_path); | |||
| } | |||
| } | |||
| /** Return the latest copy of the file at @c path that is newer. */ | |||
| char* | |||
| lilv_get_latest_copy(const char* path, const char* copy_path) | |||
| { | |||
| char* copy_dir = lilv_dirname(copy_path); | |||
| Latest latest = { lilv_strjoin(copy_path, "%u", NULL), 0, 0, NULL }; | |||
| lilv_size_mtime(path, &latest.orig_size, &latest.time); | |||
| lilv_dir_for_each(copy_dir, &latest, update_latest); | |||
| free(latest.pattern); | |||
| free(copy_dir); | |||
| return latest.latest; | |||
| } | |||
| char* | |||
| lilv_realpath(const char* path) | |||
| { | |||
| #ifdef _WIN32 | |||
| char* out = (char*)malloc(MAX_PATH); | |||
| GetFullPathName(path, MAX_PATH, out, NULL); | |||
| return out; | |||
| #else | |||
| char* real_path = realpath(path, NULL); | |||
| return real_path ? real_path : lilv_strdup(path); | |||
| #endif | |||
| } | |||
| int | |||
| lilv_symlink(const char* oldpath, const char* newpath) | |||
| { | |||
| int ret = 0; | |||
| if (strcmp(oldpath, newpath)) { | |||
| #ifdef _WIN32 | |||
| ret = 0; | |||
| #else | |||
| ret = symlink(oldpath, newpath); | |||
| #endif | |||
| } | |||
| if (ret) { | |||
| LILV_ERRORF("Failed to link %s => %s (%s)\n", | |||
| newpath, oldpath, strerror(errno)); | |||
| } | |||
| return ret; | |||
| } | |||
| char* | |||
| lilv_path_relative_to(const char* path, const char* base) | |||
| { | |||
| const size_t path_len = strlen(path); | |||
| const size_t base_len = strlen(base); | |||
| const size_t min_len = (path_len < base_len) ? path_len : base_len; | |||
| // Find the last separator common to both paths | |||
| size_t last_shared_sep = 0; | |||
| for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { | |||
| if (lilv_is_dir_sep(path[i])) { | |||
| last_shared_sep = i; | |||
| } | |||
| } | |||
| if (last_shared_sep == 0) { | |||
| // No common components, return path | |||
| return lilv_strdup(path); | |||
| } | |||
| // Find the number of up references ("..") required | |||
| size_t up = 0; | |||
| for (size_t i = last_shared_sep + 1; i < base_len; ++i) { | |||
| if (lilv_is_dir_sep(base[i])) { | |||
| ++up; | |||
| } | |||
| } | |||
| // Write up references | |||
| const size_t suffix_len = path_len - last_shared_sep; | |||
| char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1); | |||
| for (size_t i = 0; i < up; ++i) { | |||
| memcpy(rel + (i * 3), "../", 3); | |||
| } | |||
| // Write suffix | |||
| memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); | |||
| return rel; | |||
| } | |||
| bool | |||
| lilv_path_is_child(const char* path, const char* dir) | |||
| { | |||
| if (path && dir) { | |||
| const size_t path_len = strlen(path); | |||
| const size_t dir_len = strlen(dir); | |||
| return dir && path_len >= dir_len && !strncmp(path, dir, dir_len); | |||
| } | |||
| return false; | |||
| } | |||
| int | |||
| lilv_flock(FILE* file, bool lock) | |||
| { | |||
| #if defined(HAVE_FLOCK) && defined(HAVE_FILENO) | |||
| return flock(fileno(file), lock ? LOCK_EX : LOCK_UN); | |||
| #else | |||
| return 0; | |||
| #endif | |||
| } | |||
| void | |||
| lilv_dir_for_each(const char* path, | |||
| void* data, | |||
| void (*f)(const char* path, const char* name, void* data)) | |||
| { | |||
| #ifdef _WIN32 | |||
| char* pat = lilv_path_join(path, "*"); | |||
| WIN32_FIND_DATA fd; | |||
| HANDLE fh = FindFirstFile(pat, &fd); | |||
| if (fh != INVALID_HANDLE_VALUE) { | |||
| do { | |||
| f(path, fd.cFileName, data); | |||
| } while (FindNextFile(fh, &fd)); | |||
| } | |||
| free(pat); | |||
| #else | |||
| DIR* dir = opendir(path); | |||
| if (dir) { | |||
| struct dirent entry; | |||
| struct dirent* result; | |||
| while (!readdir_r(dir, &entry, &result) && result) { | |||
| f(path, entry.d_name, data); | |||
| } | |||
| closedir(dir); | |||
| } | |||
| #endif | |||
| } | |||
| int | |||
| lilv_mkdir_p(const char* dir_path) | |||
| { | |||
| char* path = lilv_strdup(dir_path); | |||
| const size_t path_len = strlen(path); | |||
| for (size_t i = 1; i <= path_len; ++i) { | |||
| if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') { | |||
| path[i] = '\0'; | |||
| if (mkdir(path, 0755) && errno != EEXIST) { | |||
| LILV_ERRORF("Failed to create %s (%s)\n", | |||
| path, strerror(errno)); | |||
| free(path); | |||
| return 1; | |||
| } | |||
| path[i] = LILV_DIR_SEP[0]; | |||
| } | |||
| } | |||
| free(path); | |||
| return 0; | |||
| } | |||
| static off_t | |||
| lilv_file_size(const char* path) | |||
| { | |||
| struct stat buf; | |||
| if (stat(path, &buf)) { | |||
| LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
| return 0; | |||
| } | |||
| return buf.st_size; | |||
| } | |||
| bool | |||
| lilv_file_equals(const char* a_path, const char* b_path) | |||
| { | |||
| if (!strcmp(a_path, b_path)) { | |||
| return true; // Paths match | |||
| } | |||
| bool match = false; | |||
| FILE* a_file = NULL; | |||
| FILE* b_file = NULL; | |||
| char* const a_real = lilv_realpath(a_path); | |||
| char* const b_real = lilv_realpath(b_path); | |||
| if (!a_real || !b_real) { | |||
| match = false; // Missing file matches nothing | |||
| } else if (!strcmp(a_real, b_real)) { | |||
| match = true; // Real paths match | |||
| } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) { | |||
| match = false; // Sizes differ | |||
| } else if (!(a_file = fopen(a_real, "rb"))) { | |||
| match = false; // Missing file matches nothing | |||
| } else if (!(b_file = fopen(b_real, "rb"))) { | |||
| match = false; // Missing file matches nothing | |||
| } else { | |||
| match = true; | |||
| // TODO: Improve performance by reading chunks | |||
| while (!feof(a_file) && !feof(b_file)) { | |||
| if (fgetc(a_file) != fgetc(b_file)) { | |||
| match = false; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| if (a_file) { | |||
| fclose(a_file); | |||
| } | |||
| if (b_file) { | |||
| fclose(b_file); | |||
| } | |||
| free(a_real); | |||
| free(b_real); | |||
| return match; | |||
| } | |||
| @@ -0,0 +1,822 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <errno.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lv2/presets.h" | |||
| #include "lilv_internal.h" | |||
| LILV_API | |||
| LilvWorld* | |||
| lilv_world_new(void) | |||
| { | |||
| LilvWorld* world = (LilvWorld*)malloc(sizeof(LilvWorld)); | |||
| world->world = sord_world_new(); | |||
| if (!world->world) | |||
| goto fail; | |||
| world->model = sord_new(world->world, SORD_SPO|SORD_OPS, true); | |||
| if (!world->model) | |||
| goto fail; | |||
| world->specs = NULL; | |||
| world->plugin_classes = lilv_plugin_classes_new(); | |||
| world->plugins = lilv_plugins_new(); | |||
| world->loaded_files = zix_tree_new( | |||
| false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free); | |||
| #ifdef LILV_NEW_LV2 | |||
| world->libs = zix_tree_new( | |||
| false, lilv_header_compare_by_uri, NULL, NULL); | |||
| #endif | |||
| #define NS_DCTERMS "http://purl.org/dc/terms/" | |||
| #define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#" | |||
| #define NEW_URI(uri) sord_new_uri(world->world, (const uint8_t*)uri) | |||
| world->uris.dc_replaces = NEW_URI(NS_DCTERMS "replaces"); | |||
| world->uris.dman_DynManifest = NEW_URI(NS_DYNMAN "DynManifest"); | |||
| world->uris.doap_name = NEW_URI(LILV_NS_DOAP "name"); | |||
| world->uris.lv2_Plugin = NEW_URI(LV2_CORE__Plugin); | |||
| world->uris.lv2_Specification = NEW_URI(LV2_CORE__Specification); | |||
| world->uris.lv2_appliesTo = NEW_URI(LV2_CORE__appliesTo); | |||
| world->uris.lv2_binary = NEW_URI(LV2_CORE__binary); | |||
| world->uris.lv2_default = NEW_URI(LV2_CORE__default); | |||
| world->uris.lv2_designation = NEW_URI(LV2_CORE__designation); | |||
| world->uris.lv2_extensionData = NEW_URI(LV2_CORE__extensionData); | |||
| world->uris.lv2_index = NEW_URI(LV2_CORE__index); | |||
| world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum); | |||
| world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum); | |||
| world->uris.lv2_name = NEW_URI(LV2_CORE__name); | |||
| world->uris.lv2_optionalFeature = NEW_URI(LV2_CORE__optionalFeature); | |||
| world->uris.lv2_port = NEW_URI(LV2_CORE__port); | |||
| world->uris.lv2_portProperty = NEW_URI(LV2_CORE__portProperty); | |||
| world->uris.lv2_reportsLatency = NEW_URI(LV2_CORE__reportsLatency); | |||
| world->uris.lv2_requiredFeature = NEW_URI(LV2_CORE__requiredFeature); | |||
| world->uris.lv2_symbol = NEW_URI(LV2_CORE__symbol); | |||
| world->uris.pset_value = NEW_URI(LV2_PRESETS__value); | |||
| world->uris.rdf_a = NEW_URI(LILV_NS_RDF "type"); | |||
| world->uris.rdf_value = NEW_URI(LILV_NS_RDF "value"); | |||
| world->uris.rdfs_Class = NEW_URI(LILV_NS_RDFS "Class"); | |||
| world->uris.rdfs_label = NEW_URI(LILV_NS_RDFS "label"); | |||
| world->uris.rdfs_seeAlso = NEW_URI(LILV_NS_RDFS "seeAlso"); | |||
| world->uris.rdfs_subClassOf = NEW_URI(LILV_NS_RDFS "subClassOf"); | |||
| world->uris.xsd_base64Binary = NEW_URI(LILV_NS_XSD "base64Binary"); | |||
| world->uris.xsd_boolean = NEW_URI(LILV_NS_XSD "boolean"); | |||
| world->uris.xsd_decimal = NEW_URI(LILV_NS_XSD "decimal"); | |||
| world->uris.xsd_double = NEW_URI(LILV_NS_XSD "double"); | |||
| world->uris.xsd_integer = NEW_URI(LILV_NS_XSD "integer"); | |||
| world->uris.null_uri = NULL; | |||
| world->lv2_plugin_class = lilv_plugin_class_new( | |||
| world, NULL, world->uris.lv2_Plugin, "Plugin"); | |||
| assert(world->lv2_plugin_class); | |||
| world->n_read_files = 0; | |||
| world->opt.filter_language = true; | |||
| world->opt.dyn_manifest = true; | |||
| return world; | |||
| fail: | |||
| /* keep on rockin' in the */ free(world); | |||
| return NULL; | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_world_free(LilvWorld* world) | |||
| { | |||
| if (!world) { | |||
| return; | |||
| } | |||
| lilv_plugin_class_free(world->lv2_plugin_class); | |||
| world->lv2_plugin_class = NULL; | |||
| for (SordNode** n = (SordNode**)&world->uris; *n; ++n) { | |||
| sord_node_free(world->world, *n); | |||
| } | |||
| for (LilvSpec* spec = world->specs; spec;) { | |||
| LilvSpec* next = spec->next; | |||
| sord_node_free(world->world, spec->spec); | |||
| sord_node_free(world->world, spec->bundle); | |||
| lilv_nodes_free(spec->data_uris); | |||
| free(spec); | |||
| spec = next; | |||
| } | |||
| world->specs = NULL; | |||
| LILV_FOREACH(plugins, i, world->plugins) { | |||
| const LilvPlugin* p = lilv_plugins_get(world->plugins, i); | |||
| lilv_plugin_free((LilvPlugin*)p); | |||
| } | |||
| zix_tree_free((ZixTree*)world->plugins); | |||
| world->plugins = NULL; | |||
| zix_tree_free((ZixTree*)world->loaded_files); | |||
| world->loaded_files = NULL; | |||
| #ifdef LILV_NEW_LV2 | |||
| zix_tree_free((ZixTree*)world->libs); | |||
| world->libs = NULL; | |||
| #endif | |||
| zix_tree_free((ZixTree*)world->plugin_classes); | |||
| world->plugin_classes = NULL; | |||
| sord_free(world->model); | |||
| world->model = NULL; | |||
| sord_world_free(world->world); | |||
| world->world = NULL; | |||
| free(world); | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_world_set_option(LilvWorld* world, | |||
| const char* option, | |||
| const LilvNode* value) | |||
| { | |||
| if (!strcmp(option, LILV_OPTION_DYN_MANIFEST)) { | |||
| if (lilv_node_is_bool(value)) { | |||
| world->opt.dyn_manifest = lilv_node_as_bool(value); | |||
| return; | |||
| } | |||
| } else if (!strcmp(option, LILV_OPTION_FILTER_LANG)) { | |||
| if (lilv_node_is_bool(value)) { | |||
| world->opt.filter_language = lilv_node_as_bool(value); | |||
| return; | |||
| } | |||
| } | |||
| LILV_WARNF("Unrecognized or invalid option `%s'\n", option); | |||
| } | |||
| LILV_API | |||
| LilvNodes* | |||
| lilv_world_find_nodes(LilvWorld* world, | |||
| const LilvNode* subject, | |||
| const LilvNode* predicate, | |||
| const LilvNode* object) | |||
| { | |||
| if (subject && !lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) { | |||
| LILV_ERRORF("Subject `%s' is not a resource\n", | |||
| sord_node_get_string(subject->node)); | |||
| return NULL; | |||
| } | |||
| if (!lilv_node_is_uri(predicate)) { | |||
| LILV_ERRORF("Predicate `%s' is not a URI\n", | |||
| sord_node_get_string(predicate->node)); | |||
| return NULL; | |||
| } | |||
| if (!subject && !object) { | |||
| LILV_ERROR("Both subject and object are NULL\n"); | |||
| return NULL; | |||
| } | |||
| SordNode* const subject_node = subject | |||
| ? sord_node_copy(subject->node) | |||
| : NULL; | |||
| SordNode* const object_node = object | |||
| ? sord_node_copy(object->node) | |||
| : NULL; | |||
| LilvNodes* ret = lilv_world_query_values_internal( | |||
| world, subject_node, predicate->node, object_node); | |||
| sord_node_free(world->world, subject_node); | |||
| sord_node_free(world->world, object_node); | |||
| return ret; | |||
| } | |||
| SordIter* | |||
| lilv_world_query_internal(LilvWorld* world, | |||
| const SordNode* subject, | |||
| const SordNode* predicate, | |||
| const SordNode* object) | |||
| { | |||
| return sord_search(world->model, subject, predicate, object, NULL); | |||
| } | |||
| LilvNodes* | |||
| lilv_world_query_values_internal(LilvWorld* world, | |||
| const SordNode* subject, | |||
| const SordNode* predicate, | |||
| const SordNode* object) | |||
| { | |||
| return lilv_nodes_from_stream_objects( | |||
| world, | |||
| lilv_world_query_internal(world, subject, predicate, object), | |||
| (object == NULL) ? SORD_OBJECT : SORD_SUBJECT); | |||
| } | |||
| static SerdNode | |||
| lilv_new_uri_relative_to_base(const uint8_t* uri_str, | |||
| const uint8_t* base_uri_str) | |||
| { | |||
| SerdURI base_uri; | |||
| if (serd_uri_parse(base_uri_str, &base_uri)) { | |||
| return SERD_NODE_NULL; | |||
| } | |||
| SerdURI ignored; | |||
| return serd_node_new_uri_from_string(uri_str, &base_uri, &ignored); | |||
| } | |||
| const uint8_t* | |||
| lilv_world_blank_node_prefix(LilvWorld* world) | |||
| { | |||
| static char str[32]; | |||
| snprintf(str, sizeof(str), "%d", world->n_read_files++); | |||
| return (const uint8_t*)str; | |||
| } | |||
| /** Comparator for sequences (e.g. world->plugins). */ | |||
| int | |||
| lilv_header_compare_by_uri(const void* a, const void* b, void* user_data) | |||
| { | |||
| const struct LilvHeader* const header_a = (const struct LilvHeader*)a; | |||
| const struct LilvHeader* const header_b = (const struct LilvHeader*)b; | |||
| return strcmp(lilv_node_as_uri(header_a->uri), | |||
| lilv_node_as_uri(header_b->uri)); | |||
| } | |||
| /** Get an element of a collection of any object with an LilvHeader by URI. */ | |||
| struct LilvHeader* | |||
| lilv_collection_get_by_uri(const ZixTree* const_seq, | |||
| const LilvNode* uri) | |||
| { | |||
| if (!lilv_node_is_uri(uri)) { | |||
| return NULL; | |||
| } | |||
| ZixTree* seq = (ZixTree*)const_seq; | |||
| struct LilvHeader key = { NULL, (LilvNode*)uri }; | |||
| ZixTreeIter* i = NULL; | |||
| ZixStatus st = zix_tree_find(seq, &key, &i); | |||
| if (!st) { | |||
| return (struct LilvHeader*)zix_tree_get(i); | |||
| } | |||
| return NULL; | |||
| } | |||
| static void | |||
| lilv_world_add_spec(LilvWorld* world, | |||
| const SordNode* specification_node, | |||
| const SordNode* bundle_node) | |||
| { | |||
| LilvSpec* spec = (LilvSpec*)malloc(sizeof(LilvSpec)); | |||
| spec->spec = sord_node_copy(specification_node); | |||
| spec->bundle = sord_node_copy(bundle_node); | |||
| spec->data_uris = lilv_nodes_new(); | |||
| // Add all plugin data files (rdfs:seeAlso) | |||
| SordIter* files = sord_search( | |||
| world->model, | |||
| specification_node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, | |||
| NULL); | |||
| FOREACH_MATCH(files) { | |||
| const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT); | |||
| zix_tree_insert((ZixTree*)spec->data_uris, | |||
| lilv_node_new_from_node(world, file_node), | |||
| NULL); | |||
| } | |||
| sord_iter_free(files); | |||
| // Add specification to world specification list | |||
| spec->next = world->specs; | |||
| world->specs = spec; | |||
| } | |||
| static void | |||
| lilv_world_add_plugin(LilvWorld* world, | |||
| const SordNode* plugin_node, | |||
| SerdNode* manifest_uri, | |||
| void* dynmanifest, | |||
| const SordNode* bundle_node) | |||
| { | |||
| LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node); | |||
| const LilvPlugin* last = lilv_plugins_get_by_uri(world->plugins, | |||
| plugin_uri); | |||
| if (last) { | |||
| LILV_ERRORF("Duplicate plugin <%s>\n", lilv_node_as_uri(plugin_uri)); | |||
| LILV_ERRORF("... found in %s\n", lilv_node_as_string( | |||
| lilv_plugin_get_bundle_uri(last))); | |||
| LILV_ERRORF("... and %s\n", sord_node_get_string(bundle_node)); | |||
| lilv_node_free(plugin_uri); | |||
| return; | |||
| } | |||
| // Create LilvPlugin | |||
| LilvNode* bundle_uri = lilv_node_new_from_node(world, bundle_node); | |||
| LilvPlugin* plugin = lilv_plugin_new(world, plugin_uri, bundle_uri); | |||
| // Add manifest as plugin data file (as if it were rdfs:seeAlso) | |||
| zix_tree_insert((ZixTree*)plugin->data_uris, | |||
| lilv_new_uri(world, (const char*)manifest_uri->buf), | |||
| NULL); | |||
| #ifdef LILV_DYN_MANIFEST | |||
| // Set dynamic manifest library URI, if applicable | |||
| if (dynmanifest) { | |||
| plugin->dynmanifest = (LilvDynManifest*)dynmanifest; | |||
| ++((LilvDynManifest*)dynmanifest)->refs; | |||
| } | |||
| #endif | |||
| // Add all plugin data files (rdfs:seeAlso) | |||
| SordIter* files = sord_search( | |||
| world->model, | |||
| plugin_node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, | |||
| NULL); | |||
| FOREACH_MATCH(files) { | |||
| const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT); | |||
| zix_tree_insert((ZixTree*)plugin->data_uris, | |||
| lilv_node_new_from_node(world, file_node), | |||
| NULL); | |||
| } | |||
| sord_iter_free(files); | |||
| // Add plugin to world plugin sequence | |||
| zix_tree_insert((ZixTree*)world->plugins, plugin, NULL); | |||
| } | |||
| static void | |||
| lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| SordNode* bundle_node, | |||
| SerdNode manifest_uri) | |||
| { | |||
| #ifdef LILV_DYN_MANIFEST | |||
| if (!world->opt.dyn_manifest) { | |||
| return; | |||
| } | |||
| typedef void* LV2_Dyn_Manifest_Handle; | |||
| LV2_Dyn_Manifest_Handle handle = NULL; | |||
| // ?dman a dynman:DynManifest | |||
| SordIter* dmanifests = sord_search( | |||
| world->model, | |||
| NULL, | |||
| world->uris.rdf_a, | |||
| world->uris.dman_DynManifest, | |||
| bundle_node); | |||
| FOREACH_MATCH(dmanifests) { | |||
| const SordNode* dmanifest = sord_iter_get_node(dmanifests, SORD_SUBJECT); | |||
| // ?dman lv2:binary ?binary | |||
| SordIter* binaries = sord_search( | |||
| world->model, | |||
| dmanifest, | |||
| world->uris.lv2_binary, | |||
| NULL, | |||
| bundle_node); | |||
| if (sord_iter_end(binaries)) { | |||
| sord_iter_free(binaries); | |||
| LILV_ERRORF("Dynamic manifest in <%s> has no binaries, ignored\n", | |||
| sord_node_get_string(bundle_node)); | |||
| continue; | |||
| } | |||
| // Get binary path | |||
| const SordNode* binary = sord_iter_get_node(binaries, SORD_OBJECT); | |||
| const uint8_t* lib_uri = sord_node_get_string(binary); | |||
| const char* lib_path = lilv_uri_to_path((const char*)lib_uri); | |||
| if (!lib_path) { | |||
| LILV_ERROR("No dynamic manifest library path\n"); | |||
| sord_iter_free(binaries); | |||
| continue; | |||
| } | |||
| // Open library | |||
| void* lib = dlopen(lib_path, RTLD_LAZY); | |||
| if (!lib) { | |||
| LILV_ERRORF("Failed to open dynmanifest library `%s'\n", lib_path); | |||
| sord_iter_free(binaries); | |||
| continue; | |||
| } | |||
| // Open dynamic manifest | |||
| typedef int (*OpenFunc)(LV2_Dyn_Manifest_Handle*, | |||
| const LV2_Feature *const *); | |||
| OpenFunc dmopen = (OpenFunc)lilv_dlfunc(lib, "lv2_dyn_manifest_open"); | |||
| if (!dmopen || dmopen(&handle, &dman_features)) { | |||
| LILV_ERRORF("No `lv2_dyn_manifest_open' in `%s'\n", lib_path); | |||
| sord_iter_free(binaries); | |||
| dlclose(lib); | |||
| continue; | |||
| } | |||
| // Get subjects (the data that would be in manifest.ttl) | |||
| typedef int (*GetSubjectsFunc)(LV2_Dyn_Manifest_Handle, FILE*); | |||
| GetSubjectsFunc get_subjects_func = (GetSubjectsFunc)lilv_dlfunc( | |||
| lib, "lv2_dyn_manifest_get_subjects"); | |||
| if (!get_subjects_func) { | |||
| LILV_ERRORF("No `lv2_dyn_manifest_get_subjects' in `%s'\n", | |||
| lib_path); | |||
| sord_iter_free(binaries); | |||
| dlclose(lib); | |||
| continue; | |||
| } | |||
| LilvDynManifest* desc = malloc(sizeof(LilvDynManifest)); | |||
| desc->bundle = lilv_node_new_from_node(world, bundle_node); | |||
| desc->lib = lib; | |||
| desc->handle = handle; | |||
| desc->refs = 0; | |||
| // Generate data file | |||
| FILE* fd = tmpfile(); | |||
| get_subjects_func(handle, fd); | |||
| rewind(fd); | |||
| // Parse generated data file | |||
| const SerdNode* base = sord_node_to_serd_node(dmanifest); | |||
| SerdEnv* env = serd_env_new(base); | |||
| SerdReader* reader = sord_new_reader( | |||
| world->model, env, SERD_TURTLE, sord_node_copy(dmanifest)); | |||
| serd_reader_add_blank_prefix(reader, | |||
| lilv_world_blank_node_prefix(world)); | |||
| serd_reader_read_file_handle(reader, fd, | |||
| (const uint8_t*)"(dyn-manifest)"); | |||
| serd_reader_free(reader); | |||
| serd_env_free(env); | |||
| // Close (and automatically delete) temporary data file | |||
| fclose(fd); | |||
| // ?plugin a lv2:Plugin | |||
| SordIter* plug_results = sord_search( | |||
| world->model, | |||
| NULL, | |||
| world->uris.rdf_a, | |||
| world->uris.lv2_Plugin, | |||
| dmanifest); | |||
| FOREACH_MATCH(plug_results) { | |||
| const SordNode* plugin_node = sord_iter_get_node(plug_results, SORD_SUBJECT); | |||
| lilv_world_add_plugin(world, plugin_node, | |||
| &manifest_uri, desc, bundle_node); | |||
| } | |||
| sord_iter_free(plug_results); | |||
| sord_iter_free(binaries); | |||
| } | |||
| sord_iter_free(dmanifests); | |||
| #endif // LILV_DYN_MANIFEST | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) | |||
| { | |||
| if (!lilv_node_is_uri(bundle_uri)) { | |||
| LILV_ERRORF("Bundle URI `%s' is not a URI\n", | |||
| sord_node_get_string(bundle_uri->node)); | |||
| return; | |||
| } | |||
| SordNode* bundle_node = bundle_uri->node; | |||
| SerdNode manifest_uri = lilv_new_uri_relative_to_base( | |||
| (const uint8_t*)"manifest.ttl", | |||
| (const uint8_t*)sord_node_get_string(bundle_node)); | |||
| SerdEnv* env = serd_env_new(&manifest_uri); | |||
| SerdReader* reader = sord_new_reader(world->model, env, SERD_TURTLE, | |||
| bundle_node); | |||
| serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world)); | |||
| SerdStatus st = serd_reader_read_file(reader, manifest_uri.buf); | |||
| serd_reader_free(reader); | |||
| serd_env_free(env); | |||
| if (st) { | |||
| LILV_ERRORF("Error reading %s\n", manifest_uri.buf); | |||
| return; | |||
| } | |||
| // ?plugin a lv2:Plugin | |||
| SordIter* plug_results = sord_search( | |||
| world->model, | |||
| NULL, | |||
| world->uris.rdf_a, | |||
| world->uris.lv2_Plugin, | |||
| bundle_node); | |||
| FOREACH_MATCH(plug_results) { | |||
| const SordNode* plugin_node = sord_iter_get_node(plug_results, SORD_SUBJECT); | |||
| lilv_world_add_plugin(world, plugin_node, | |||
| &manifest_uri, NULL, bundle_node); | |||
| } | |||
| sord_iter_free(plug_results); | |||
| lilv_world_load_dyn_manifest(world, bundle_node, manifest_uri); | |||
| // ?specification a lv2:Specification | |||
| SordIter* spec_results = sord_search( | |||
| world->model, | |||
| NULL, | |||
| world->uris.rdf_a, | |||
| world->uris.lv2_Specification, | |||
| bundle_node); | |||
| FOREACH_MATCH(spec_results) { | |||
| const SordNode* spec = sord_iter_get_node(spec_results, SORD_SUBJECT); | |||
| lilv_world_add_spec(world, spec, bundle_node); | |||
| } | |||
| sord_iter_free(spec_results); | |||
| serd_node_free(&manifest_uri); | |||
| } | |||
| static void | |||
| load_dir_entry(const char* dir, const char* name, void* data) | |||
| { | |||
| LilvWorld* world = (LilvWorld*)data; | |||
| if (!strcmp(name, ".") || !strcmp(name, "..")) | |||
| return; | |||
| const char* scheme = (dir[0] == '/') ? "file://" : "file:///"; | |||
| char* uri = lilv_strjoin(scheme, dir, "/", name, "/", NULL); | |||
| LilvNode* uri_val = lilv_new_uri(world, uri); | |||
| lilv_world_load_bundle(world, uri_val); | |||
| lilv_node_free(uri_val); | |||
| free(uri); | |||
| } | |||
| /** Load all bundles in the directory at @a dir_path. */ | |||
| static void | |||
| lilv_world_load_directory(LilvWorld* world, const char* dir_path) | |||
| { | |||
| char* path = lilv_expand(dir_path); | |||
| if (!path) { | |||
| LILV_WARNF("Empty path `%s'\n", path); | |||
| return; | |||
| } | |||
| lilv_dir_for_each(path, world, load_dir_entry); | |||
| free(path); | |||
| } | |||
| static bool | |||
| is_path_sep(char c) | |||
| { | |||
| return c == LILV_PATH_SEP[0]; | |||
| } | |||
| static const char* | |||
| first_path_sep(const char* path) | |||
| { | |||
| for (const char* p = path; *p != '\0'; ++p) { | |||
| if (is_path_sep(*p)) { | |||
| return p; | |||
| } | |||
| } | |||
| return NULL; | |||
| } | |||
| /** Load all bundles found in @a lv2_path. | |||
| * @param lv2_path A colon-delimited list of directories. These directories | |||
| * should contain LV2 bundle directories (ie the search path is a list of | |||
| * parent directories of bundles, not a list of bundle directories). | |||
| */ | |||
| static void | |||
| lilv_world_load_path(LilvWorld* world, | |||
| const char* lv2_path) | |||
| { | |||
| while (lv2_path[0] != '\0') { | |||
| const char* const sep = first_path_sep(lv2_path); | |||
| if (sep) { | |||
| const size_t dir_len = sep - lv2_path; | |||
| char* const dir = (char*)malloc(dir_len + 1); | |||
| memcpy(dir, lv2_path, dir_len); | |||
| dir[dir_len] = '\0'; | |||
| lilv_world_load_directory(world, dir); | |||
| free(dir); | |||
| lv2_path += dir_len + 1; | |||
| } else { | |||
| lilv_world_load_directory(world, lv2_path); | |||
| lv2_path = "\0"; | |||
| } | |||
| } | |||
| } | |||
| static void | |||
| lilv_world_load_specifications(LilvWorld* world) | |||
| { | |||
| for (LilvSpec* spec = world->specs; spec; spec = spec->next) { | |||
| LILV_FOREACH(nodes, f, spec->data_uris) { | |||
| LilvNode* file = (LilvNode*)lilv_collection_get(spec->data_uris, f); | |||
| const SerdNode* node = sord_node_to_serd_node(file->node); | |||
| SerdEnv* env = serd_env_new(node); | |||
| SerdReader* reader = sord_new_reader(world->model, env, | |||
| SERD_TURTLE, NULL); | |||
| serd_reader_add_blank_prefix(reader, | |||
| lilv_world_blank_node_prefix(world)); | |||
| serd_reader_read_file(reader, node->buf); | |||
| serd_reader_free(reader); | |||
| serd_env_free(env); | |||
| } | |||
| } | |||
| } | |||
| static void | |||
| lilv_world_load_plugin_classes(LilvWorld* world) | |||
| { | |||
| /* FIXME: This loads all classes, not just lv2:Plugin subclasses. | |||
| However, if the host gets all the classes via lilv_plugin_class_get_children | |||
| starting with lv2:Plugin as the root (which is e.g. how a host would build | |||
| a menu), they won't be seen anyway... | |||
| */ | |||
| SordIter* classes = sord_search( | |||
| world->model, | |||
| NULL, | |||
| world->uris.rdf_a, | |||
| world->uris.rdfs_Class, | |||
| NULL); | |||
| FOREACH_MATCH(classes) { | |||
| const SordNode* class_node = sord_iter_get_node(classes, SORD_SUBJECT); | |||
| // Get parents (superclasses) | |||
| SordIter* parents = sord_search( | |||
| world->model, | |||
| class_node, | |||
| world->uris.rdfs_subClassOf, | |||
| NULL, | |||
| NULL); | |||
| if (sord_iter_end(parents)) { | |||
| sord_iter_free(parents); | |||
| continue; | |||
| } | |||
| const SordNode* parent_node = sord_iter_get_node(parents, SORD_OBJECT); | |||
| sord_iter_free(parents); | |||
| if (!sord_node_get_type(parent_node) == SORD_URI) { | |||
| // Class parent is not a resource, ignore (e.g. owl restriction) | |||
| continue; | |||
| } | |||
| // Get labels | |||
| SordIter* labels = sord_search( | |||
| world->model, | |||
| class_node, | |||
| world->uris.rdfs_label, | |||
| NULL, | |||
| NULL); | |||
| if (sord_iter_end(labels)) { | |||
| sord_iter_free(labels); | |||
| continue; | |||
| } | |||
| const SordNode* label_node = sord_iter_get_node(labels, SORD_OBJECT); | |||
| const uint8_t* label = sord_node_get_string(label_node); | |||
| sord_iter_free(labels); | |||
| LilvPluginClass* pclass = lilv_plugin_class_new( | |||
| world, parent_node, class_node, (const char*)label); | |||
| if (pclass) { | |||
| zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL); | |||
| } | |||
| } | |||
| sord_iter_free(classes); | |||
| } | |||
| LILV_API | |||
| void | |||
| lilv_world_load_all(LilvWorld* world) | |||
| { | |||
| const char* lv2_path = getenv("LV2_PATH"); | |||
| if (!lv2_path) | |||
| lv2_path = LILV_DEFAULT_LV2_PATH; | |||
| // Discover bundles and read all manifest files into model | |||
| lilv_world_load_path(world, lv2_path); | |||
| LILV_FOREACH(plugins, p, world->plugins) { | |||
| const LilvPlugin* plugin = (const LilvPlugin*)lilv_collection_get( | |||
| (ZixTree*)world->plugins, p); | |||
| // ?new dc:replaces plugin | |||
| SordIter* replacement = sord_search( | |||
| world->model, | |||
| NULL, | |||
| world->uris.dc_replaces, | |||
| lilv_node_as_node(lilv_plugin_get_uri(plugin)), | |||
| NULL); | |||
| if (!sord_iter_end(replacement)) { | |||
| /* TODO: Check if replacement is actually a known plugin, | |||
| though this is expensive... | |||
| */ | |||
| ((LilvPlugin*)plugin)->replaced = true; | |||
| } | |||
| sord_iter_free(replacement); | |||
| } | |||
| // Query out things to cache | |||
| lilv_world_load_specifications(world); | |||
| lilv_world_load_plugin_classes(world); | |||
| } | |||
| LILV_API | |||
| int | |||
| lilv_world_load_resource(LilvWorld* world, | |||
| const LilvNode* resource) | |||
| { | |||
| if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) { | |||
| LILV_ERRORF("Node `%s' is not a resource\n", | |||
| sord_node_get_string(resource->node)); | |||
| return -1; | |||
| } | |||
| int n_read = 0; | |||
| SordIter* files = sord_search(world->model, | |||
| resource->node, | |||
| world->uris.rdfs_seeAlso, | |||
| NULL, NULL); | |||
| FOREACH_MATCH(files) { | |||
| const SordNode* file = sord_iter_get_node(files, SORD_OBJECT); | |||
| const uint8_t* str = sord_node_get_string(file); | |||
| LilvNode* file_node = lilv_node_new_from_node(world, file); | |||
| ZixTreeIter* iter; | |||
| if (zix_tree_find((ZixTree*)world->loaded_files, file_node, &iter)) { | |||
| if (sord_node_get_type(file) == SORD_URI) { | |||
| const SerdNode* base = sord_node_to_serd_node(file); | |||
| SerdEnv* env = serd_env_new(base); | |||
| SerdReader* reader = sord_new_reader( | |||
| world->model, env, SERD_TURTLE, (SordNode*)file); | |||
| serd_reader_add_blank_prefix( | |||
| reader, lilv_world_blank_node_prefix(world)); | |||
| if (!serd_reader_read_file(reader, str)) { | |||
| ++n_read; | |||
| zix_tree_insert( | |||
| (ZixTree*)world->loaded_files, file_node, NULL); | |||
| file_node = NULL; // prevent deletion... | |||
| } else { | |||
| LILV_ERRORF("Error loading resource `%s'\n", str); | |||
| } | |||
| serd_reader_free(reader); | |||
| serd_env_free(env); | |||
| } else { | |||
| LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", str); | |||
| } | |||
| } | |||
| lilv_node_free(file_node); // ...here | |||
| } | |||
| sord_iter_free(files); | |||
| return n_read; | |||
| } | |||
| LILV_API | |||
| const LilvPluginClass* | |||
| lilv_world_get_plugin_class(const LilvWorld* world) | |||
| { | |||
| return world->lv2_plugin_class; | |||
| } | |||
| LILV_API | |||
| const LilvPluginClasses* | |||
| lilv_world_get_plugin_classes(const LilvWorld* world) | |||
| { | |||
| return world->plugin_classes; | |||
| } | |||
| LILV_API | |||
| const LilvPlugins* | |||
| lilv_world_get_all_plugins(const LilvWorld* world) | |||
| { | |||
| return world->plugins; | |||
| } | |||
| @@ -0,0 +1,88 @@ | |||
| /* | |||
| Copyright 2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef ZIX_COMMON_H | |||
| #define ZIX_COMMON_H | |||
| /** | |||
| @addtogroup zix | |||
| @{ | |||
| */ | |||
| /** @cond */ | |||
| #ifdef ZIX_SHARED | |||
| # ifdef _WIN32 | |||
| # define ZIX_LIB_IMPORT __declspec(dllimport) | |||
| # define ZIX_LIB_EXPORT __declspec(dllexport) | |||
| # else | |||
| # define ZIX_LIB_IMPORT __attribute__((visibility("default"))) | |||
| # define ZIX_LIB_EXPORT __attribute__((visibility("default"))) | |||
| # endif | |||
| # ifdef ZIX_INTERNAL | |||
| # define ZIX_API ZIX_LIB_EXPORT | |||
| # else | |||
| # define ZIX_API ZIX_LIB_IMPORT | |||
| # endif | |||
| # define ZIX_PRIVATE static | |||
| #elif defined(ZIX_INLINE) | |||
| # define ZIX_API static inline | |||
| # define ZIX_PRIVATE static inline | |||
| #else | |||
| # define ZIX_API | |||
| # define ZIX_PRIVATE static | |||
| #endif | |||
| /** @endcond */ | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #else | |||
| # include <stdbool.h> | |||
| #endif | |||
| typedef enum { | |||
| ZIX_STATUS_SUCCESS, | |||
| ZIX_STATUS_ERROR, | |||
| ZIX_STATUS_NO_MEM, | |||
| ZIX_STATUS_NOT_FOUND, | |||
| ZIX_STATUS_EXISTS, | |||
| ZIX_STATUS_BAD_ARG, | |||
| ZIX_STATUS_BAD_PERMS, | |||
| } ZixStatus; | |||
| /** | |||
| Function for comparing two elements. | |||
| */ | |||
| typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); | |||
| /** | |||
| Function for testing equality of two elements. | |||
| */ | |||
| typedef bool (*ZixEqualFunc)(const void* a, const void* b); | |||
| /** | |||
| Function to destroy an element. | |||
| */ | |||
| typedef void (*ZixDestroyFunc)(void* ptr); | |||
| /** | |||
| @} | |||
| */ | |||
| #ifdef __cplusplus | |||
| } /* extern "C" */ | |||
| #endif | |||
| #endif /* ZIX_COMMON_H */ | |||
| @@ -0,0 +1,716 @@ | |||
| /* | |||
| Copyright 2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <stdint.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "zix/common.h" | |||
| #include "zix/tree.h" | |||
| typedef struct ZixTreeNodeImpl ZixTreeNode; | |||
| struct ZixTreeImpl { | |||
| ZixTreeNode* root; | |||
| ZixDestroyFunc destroy; | |||
| ZixComparator cmp; | |||
| void* cmp_data; | |||
| size_t size; | |||
| bool allow_duplicates; | |||
| }; | |||
| struct ZixTreeNodeImpl { | |||
| void* data; | |||
| struct ZixTreeNodeImpl* left; | |||
| struct ZixTreeNodeImpl* right; | |||
| struct ZixTreeNodeImpl* parent; | |||
| int_fast8_t balance; | |||
| }; | |||
| #define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||
| #define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |||
| // Uncomment these for debugging features | |||
| // #define ZIX_TREE_DUMP 1 | |||
| // #define ZIX_TREE_VERIFY 1 | |||
| // #define ZIX_TREE_HYPER_VERIFY 1 | |||
| #if defined(ZIX_TREE_VERIFY) || defined(ZIX_TREE_HYPER_VERIFY) | |||
| # include "tree_debug.h" | |||
| # define ASSERT_BALANCE(n) assert(verify_balance(n)) | |||
| #else | |||
| # define ASSERT_BALANCE(n) | |||
| #endif | |||
| #ifdef ZIX_TREE_DUMP | |||
| # include "tree_debug.h" | |||
| # define DUMP(t) zix_tree_print(t->root, 0) | |||
| # define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__) | |||
| #else | |||
| # define DUMP(t) | |||
| # define DEBUG_PRINTF(fmt, ...) | |||
| #endif | |||
| ZIX_API ZixTree* | |||
| zix_tree_new(bool allow_duplicates, | |||
| ZixComparator cmp, | |||
| void* cmp_data, | |||
| ZixDestroyFunc destroy) | |||
| { | |||
| ZixTree* t = (ZixTree*)malloc(sizeof(ZixTree)); | |||
| t->root = NULL; | |||
| t->destroy = destroy; | |||
| t->cmp = cmp; | |||
| t->cmp_data = cmp_data; | |||
| t->size = 0; | |||
| t->allow_duplicates = allow_duplicates; | |||
| return t; | |||
| } | |||
| ZIX_PRIVATE void | |||
| zix_tree_free_rec(ZixTree* t, ZixTreeNode* n) | |||
| { | |||
| if (n) { | |||
| zix_tree_free_rec(t, n->left); | |||
| zix_tree_free_rec(t, n->right); | |||
| if (t->destroy) { | |||
| t->destroy(n->data); | |||
| } | |||
| free(n); | |||
| } | |||
| } | |||
| ZIX_API void | |||
| zix_tree_free(ZixTree* t) | |||
| { | |||
| if (t) { | |||
| zix_tree_free_rec(t, t->root); | |||
| free(t); | |||
| } | |||
| } | |||
| ZIX_API size_t | |||
| zix_tree_size(const ZixTree* t) | |||
| { | |||
| return t->size; | |||
| } | |||
| ZIX_PRIVATE void | |||
| rotate(ZixTreeNode* p, ZixTreeNode* q) | |||
| { | |||
| assert(q->parent == p); | |||
| assert(p->left == q || p->right == q); | |||
| q->parent = p->parent; | |||
| if (q->parent) { | |||
| if (q->parent->left == p) { | |||
| q->parent->left = q; | |||
| } else { | |||
| q->parent->right = q; | |||
| } | |||
| } | |||
| if (p->right == q) { | |||
| // Rotate left | |||
| p->right = q->left; | |||
| q->left = p; | |||
| if (p->right) { | |||
| p->right->parent = p; | |||
| } | |||
| } else { | |||
| // Rotate right | |||
| assert(p->left == q); | |||
| p->left = q->right; | |||
| q->right = p; | |||
| if (p->left) { | |||
| p->left->parent = p; | |||
| } | |||
| } | |||
| p->parent = q; | |||
| } | |||
| /** | |||
| * Rotate left about @a p. | |||
| * | |||
| * p q | |||
| * / \ / \ | |||
| * A q => p C | |||
| * / \ / \ | |||
| * B C A B | |||
| */ | |||
| ZIX_PRIVATE ZixTreeNode* | |||
| rotate_left(ZixTreeNode* p, int* height_change) | |||
| { | |||
| ZixTreeNode* const q = p->right; | |||
| *height_change = (q->balance == 0) ? 0 : -1; | |||
| DEBUG_PRINTF("LL %ld\n", (intptr_t)p->data); | |||
| assert(p->balance == 2); | |||
| assert(q->balance == 0 || q->balance == 1); | |||
| rotate(p, q); | |||
| // p->balance -= 1 + MAX(0, q->balance); | |||
| // q->balance -= 1 - MIN(0, p->balance); | |||
| --q->balance; | |||
| p->balance = -(q->balance); | |||
| ASSERT_BALANCE(p); | |||
| ASSERT_BALANCE(q); | |||
| return q; | |||
| } | |||
| /** | |||
| * Rotate right about @a p. | |||
| * | |||
| * p q | |||
| * / \ / \ | |||
| * q C => A p | |||
| * / \ / \ | |||
| * A B B C | |||
| * | |||
| */ | |||
| ZIX_PRIVATE ZixTreeNode* | |||
| rotate_right(ZixTreeNode* p, int* height_change) | |||
| { | |||
| ZixTreeNode* const q = p->left; | |||
| *height_change = (q->balance == 0) ? 0 : -1; | |||
| DEBUG_PRINTF("RR %ld\n", (intptr_t)p->data); | |||
| assert(p->balance == -2); | |||
| assert(q->balance == 0 || q->balance == -1); | |||
| rotate(p, q); | |||
| // p->balance += 1 - MIN(0, q->balance); | |||
| // q->balance += 1 + MAX(0, p->balance); | |||
| ++q->balance; | |||
| p->balance = -(q->balance); | |||
| ASSERT_BALANCE(p); | |||
| ASSERT_BALANCE(q); | |||
| return q; | |||
| } | |||
| /** | |||
| * Rotate left about @a p->left then right about @a p. | |||
| * | |||
| * p r | |||
| * / \ / \ | |||
| * q D => q p | |||
| * / \ / \ / \ | |||
| * A r A B C D | |||
| * / \ | |||
| * B C | |||
| * | |||
| */ | |||
| ZIX_PRIVATE ZixTreeNode* | |||
| rotate_left_right(ZixTreeNode* p, int* height_change) | |||
| { | |||
| ZixTreeNode* const q = p->left; | |||
| ZixTreeNode* const r = q->right; | |||
| assert(p->balance == -2); | |||
| assert(q->balance == 1); | |||
| assert(r->balance == -1 || r->balance == 0 || r->balance == 1); | |||
| DEBUG_PRINTF("LR %ld P: %2d Q: %2d R: %2d\n", | |||
| (intptr_t)p->data, p->balance, q->balance, r->balance); | |||
| rotate(q, r); | |||
| rotate(p, r); | |||
| q->balance -= 1 + MAX(0, r->balance); | |||
| p->balance += 1 - MIN(MIN(0, r->balance) - 1, r->balance + q->balance); | |||
| // r->balance += MAX(0, p->balance) + MIN(0, q->balance); | |||
| // p->balance = (p->left && p->right) ? -MIN(r->balance, 0) : 0; | |||
| // q->balance = - MAX(r->balance, 0); | |||
| r->balance = 0; | |||
| *height_change = -1; | |||
| ASSERT_BALANCE(p); | |||
| ASSERT_BALANCE(q); | |||
| ASSERT_BALANCE(r); | |||
| return r; | |||
| } | |||
| /** | |||
| * Rotate right about @a p->right then right about @a p. | |||
| * | |||
| * p r | |||
| * / \ / \ | |||
| * A q => p q | |||
| * / \ / \ / \ | |||
| * r D A B C D | |||
| * / \ | |||
| * B C | |||
| * | |||
| */ | |||
| ZIX_PRIVATE ZixTreeNode* | |||
| rotate_right_left(ZixTreeNode* p, int* height_change) | |||
| { | |||
| ZixTreeNode* const q = p->right; | |||
| ZixTreeNode* const r = q->left; | |||
| assert(p->balance == 2); | |||
| assert(q->balance == -1); | |||
| assert(r->balance == -1 || r->balance == 0 || r->balance == 1); | |||
| DEBUG_PRINTF("RL %ld P: %2d Q: %2d R: %2d\n", | |||
| (intptr_t)p->data, p->balance, q->balance, r->balance); | |||
| rotate(q, r); | |||
| rotate(p, r); | |||
| q->balance += 1 - MIN(0, r->balance); | |||
| p->balance -= 1 + MAX(MAX(0, r->balance) + 1, r->balance + q->balance); | |||
| // r->balance += MAX(0, q->balance) + MIN(0, p->balance); | |||
| // p->balance = (p->left && p->right) ? -MAX(r->balance, 0) : 0; | |||
| // q->balance = - MIN(r->balance, 0); | |||
| r->balance = 0; | |||
| // assert(r->balance == 0); | |||
| *height_change = -1; | |||
| ASSERT_BALANCE(p); | |||
| ASSERT_BALANCE(q); | |||
| ASSERT_BALANCE(r); | |||
| return r; | |||
| } | |||
| ZIX_PRIVATE ZixTreeNode* | |||
| zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) | |||
| { | |||
| #ifdef ZIX_TREE_HYPER_VERIFY | |||
| const size_t old_height = height(node); | |||
| #endif | |||
| DEBUG_PRINTF("REBALANCE %ld (%d)\n", (intptr_t)node->data, node->balance); | |||
| *height_change = 0; | |||
| const bool is_root = !node->parent; | |||
| assert((is_root && t->root == node) || (!is_root && t->root != node)); | |||
| ZixTreeNode* replacement = node; | |||
| if (node->balance == -2) { | |||
| assert(node->left); | |||
| if (node->left->balance == 1) { | |||
| replacement = rotate_left_right(node, height_change); | |||
| } else { | |||
| replacement = rotate_right(node, height_change); | |||
| } | |||
| } else if (node->balance == 2) { | |||
| assert(node->right); | |||
| if (node->right->balance == -1) { | |||
| replacement = rotate_right_left(node, height_change); | |||
| } else { | |||
| replacement = rotate_left(node, height_change); | |||
| } | |||
| } | |||
| if (is_root) { | |||
| assert(!replacement->parent); | |||
| t->root = replacement; | |||
| } | |||
| DUMP(t); | |||
| #ifdef ZIX_TREE_HYPER_VERIFY | |||
| assert(old_height + *height_change == height(replacement)); | |||
| #endif | |||
| return replacement; | |||
| } | |||
| ZIX_API ZixStatus | |||
| zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti) | |||
| { | |||
| DEBUG_PRINTF("**** INSERT %ld\n", (intptr_t)e); | |||
| int cmp = 0; | |||
| ZixTreeNode* n = t->root; | |||
| ZixTreeNode* p = NULL; | |||
| // Find the parent p of e | |||
| while (n) { | |||
| p = n; | |||
| cmp = t->cmp(e, n->data, t->cmp_data); | |||
| if (cmp < 0) { | |||
| n = n->left; | |||
| } else if (cmp > 0) { | |||
| n = n->right; | |||
| } else if (t->allow_duplicates) { | |||
| n = n->right; | |||
| } else { | |||
| if (ti) { | |||
| *ti = n; | |||
| } | |||
| DEBUG_PRINTF("%ld EXISTS!\n", (intptr_t)e); | |||
| return ZIX_STATUS_EXISTS; | |||
| } | |||
| } | |||
| // Allocate a new node n | |||
| if (!(n = (ZixTreeNode*)malloc(sizeof(ZixTreeNode)))) { | |||
| return ZIX_STATUS_NO_MEM; | |||
| } | |||
| memset(n, '\0', sizeof(ZixTreeNode)); | |||
| n->data = e; | |||
| n->balance = 0; | |||
| if (ti) { | |||
| *ti = n; | |||
| } | |||
| bool p_height_increased = false; | |||
| // Make p the parent of n | |||
| n->parent = p; | |||
| if (!p) { | |||
| t->root = n; | |||
| } else { | |||
| if (cmp < 0) { | |||
| assert(!p->left); | |||
| assert(p->balance == 0 || p->balance == 1); | |||
| p->left = n; | |||
| --p->balance; | |||
| p_height_increased = !p->right; | |||
| } else { | |||
| assert(!p->right); | |||
| assert(p->balance == 0 || p->balance == -1); | |||
| p->right = n; | |||
| ++p->balance; | |||
| p_height_increased = !p->left; | |||
| } | |||
| } | |||
| DUMP(t); | |||
| // Rebalance if necessary (at most 1 rotation) | |||
| assert(!p || p->balance == -1 || p->balance == 0 || p->balance == 1); | |||
| if (p && p_height_increased) { | |||
| int height_change = 0; | |||
| for (ZixTreeNode* i = p; i && i->parent; i = i->parent) { | |||
| if (i == i->parent->left) { | |||
| if (--i->parent->balance == -2) { | |||
| zix_tree_rebalance(t, i->parent, &height_change); | |||
| break; | |||
| } | |||
| } else { | |||
| assert(i == i->parent->right); | |||
| if (++i->parent->balance == 2) { | |||
| zix_tree_rebalance(t, i->parent, &height_change); | |||
| break; | |||
| } | |||
| } | |||
| if (i->parent->balance == 0) { | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| DUMP(t); | |||
| ++t->size; | |||
| #ifdef ZIX_TREE_VERIFY | |||
| if (!verify(t, t->root)) { | |||
| return ZIX_STATUS_ERROR; | |||
| } | |||
| #endif | |||
| return ZIX_STATUS_SUCCESS; | |||
| } | |||
| ZIX_API ZixStatus | |||
| zix_tree_remove(ZixTree* t, ZixTreeIter* ti) | |||
| { | |||
| ZixTreeNode* const n = ti; | |||
| ZixTreeNode** pp = NULL; // parent pointer | |||
| ZixTreeNode* to_balance = n->parent; // lowest node to balance | |||
| int8_t d_balance = 0; // delta(balance) for n->parent | |||
| DEBUG_PRINTF("*** REMOVE %ld\n", (intptr_t)n->data); | |||
| if ((n == t->root) && !n->left && !n->right) { | |||
| t->root = NULL; | |||
| if (t->destroy) { | |||
| t->destroy(n->data); | |||
| } | |||
| free(n); | |||
| --t->size; | |||
| assert(t->size == 0); | |||
| return ZIX_STATUS_SUCCESS; | |||
| } | |||
| // Set pp to the parent pointer to n, if applicable | |||
| if (n->parent) { | |||
| assert(n->parent->left == n || n->parent->right == n); | |||
| if (n->parent->left == n) { // n is left child | |||
| pp = &n->parent->left; | |||
| d_balance = 1; | |||
| } else { // n is right child | |||
| assert(n->parent->right == n); | |||
| pp = &n->parent->right; | |||
| d_balance = -1; | |||
| } | |||
| } | |||
| assert(!pp || *pp == n); | |||
| int height_change = 0; | |||
| if (!n->left && !n->right) { | |||
| // n is a leaf, just remove it | |||
| if (pp) { | |||
| *pp = NULL; | |||
| to_balance = n->parent; | |||
| height_change = (!n->parent->left && !n->parent->right) ? -1 : 0; | |||
| } | |||
| } else if (!n->left) { | |||
| // Replace n with right (only) child | |||
| if (pp) { | |||
| *pp = n->right; | |||
| to_balance = n->parent; | |||
| } else { | |||
| t->root = n->right; | |||
| } | |||
| n->right->parent = n->parent; | |||
| height_change = -1; | |||
| } else if (!n->right) { | |||
| // Replace n with left (only) child | |||
| if (pp) { | |||
| *pp = n->left; | |||
| to_balance = n->parent; | |||
| } else { | |||
| t->root = n->left; | |||
| } | |||
| n->left->parent = n->parent; | |||
| height_change = -1; | |||
| } else { | |||
| // Replace n with in-order successor (leftmost child of right subtree) | |||
| ZixTreeNode* replace = n->right; | |||
| while (replace->left) { | |||
| assert(replace->left->parent == replace); | |||
| replace = replace->left; | |||
| } | |||
| // Remove replace from parent (replace_p) | |||
| if (replace->parent->left == replace) { | |||
| height_change = replace->parent->right ? 0 : -1; | |||
| d_balance = 1; | |||
| to_balance = replace->parent; | |||
| replace->parent->left = replace->right; | |||
| } else { | |||
| assert(replace->parent == n); | |||
| height_change = replace->parent->left ? 0 : -1; | |||
| d_balance = -1; | |||
| to_balance = replace->parent; | |||
| replace->parent->right = replace->right; | |||
| } | |||
| if (to_balance == n) { | |||
| to_balance = replace; | |||
| } | |||
| if (replace->right) { | |||
| replace->right->parent = replace->parent; | |||
| } | |||
| replace->balance = n->balance; | |||
| // Swap node to delete with replace | |||
| if (pp) { | |||
| *pp = replace; | |||
| } else { | |||
| assert(t->root == n); | |||
| t->root = replace; | |||
| } | |||
| replace->parent = n->parent; | |||
| replace->left = n->left; | |||
| n->left->parent = replace; | |||
| replace->right = n->right; | |||
| if (n->right) { | |||
| n->right->parent = replace; | |||
| } | |||
| assert(!replace->parent | |||
| || replace->parent->left == replace | |||
| || replace->parent->right == replace); | |||
| } | |||
| // Rebalance starting at to_balance upwards. | |||
| for (ZixTreeNode* i = to_balance; i; i = i->parent) { | |||
| i->balance += d_balance; | |||
| if (d_balance == 0 || i->balance == -1 || i->balance == 1) { | |||
| break; | |||
| } | |||
| assert(i != n); | |||
| i = zix_tree_rebalance(t, i, &height_change); | |||
| if (i->balance == 0) { | |||
| height_change = -1; | |||
| } | |||
| if (i->parent) { | |||
| if (i == i->parent->left) { | |||
| d_balance = height_change * -1; | |||
| } else { | |||
| assert(i == i->parent->right); | |||
| d_balance = height_change; | |||
| } | |||
| } | |||
| } | |||
| DUMP(t); | |||
| if (t->destroy) { | |||
| t->destroy(n->data); | |||
| } | |||
| free(n); | |||
| --t->size; | |||
| #ifdef ZIX_TREE_VERIFY | |||
| if (!verify(t, t->root)) { | |||
| return ZIX_STATUS_ERROR; | |||
| } | |||
| #endif | |||
| return ZIX_STATUS_SUCCESS; | |||
| } | |||
| ZIX_API ZixStatus | |||
| zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti) | |||
| { | |||
| ZixTreeNode* n = t->root; | |||
| while (n) { | |||
| const int cmp = t->cmp(e, n->data, t->cmp_data); | |||
| if (cmp == 0) { | |||
| break; | |||
| } else if (cmp < 0) { | |||
| n = n->left; | |||
| } else { | |||
| n = n->right; | |||
| } | |||
| } | |||
| *ti = n; | |||
| return (n) ? ZIX_STATUS_SUCCESS : ZIX_STATUS_NOT_FOUND; | |||
| } | |||
| ZIX_API void* | |||
| zix_tree_get(ZixTreeIter* ti) | |||
| { | |||
| return ti ? ti->data : NULL; | |||
| } | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_begin(ZixTree* t) | |||
| { | |||
| if (!t->root) { | |||
| return NULL; | |||
| } | |||
| ZixTreeNode* n = t->root; | |||
| while (n->left) { | |||
| n = n->left; | |||
| } | |||
| return n; | |||
| } | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_end(ZixTree* t) | |||
| { | |||
| return NULL; | |||
| } | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_rbegin(ZixTree* t) | |||
| { | |||
| if (!t->root) { | |||
| return NULL; | |||
| } | |||
| ZixTreeNode* n = t->root; | |||
| while (n->right) { | |||
| n = n->right; | |||
| } | |||
| return n; | |||
| } | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_rend(ZixTree* t) | |||
| { | |||
| return NULL; | |||
| } | |||
| ZIX_API bool | |||
| zix_tree_iter_is_end(ZixTreeIter* i) | |||
| { | |||
| return !i; | |||
| } | |||
| ZIX_API bool | |||
| zix_tree_iter_is_rend(ZixTreeIter* i) | |||
| { | |||
| return !i; | |||
| } | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_iter_next(ZixTreeIter* i) | |||
| { | |||
| if (!i) { | |||
| return NULL; | |||
| } | |||
| if (i->right) { | |||
| i = i->right; | |||
| while (i->left) { | |||
| i = i->left; | |||
| } | |||
| } else { | |||
| while (i->parent && i->parent->right == i) { // i is a right child | |||
| i = i->parent; | |||
| } | |||
| i = i->parent; | |||
| } | |||
| return i; | |||
| } | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_iter_prev(ZixTreeIter* i) | |||
| { | |||
| if (!i) { | |||
| return NULL; | |||
| } | |||
| if (i->left) { | |||
| i = i->left; | |||
| while (i->right) { | |||
| i = i->right; | |||
| } | |||
| } else { | |||
| while (i->parent && i->parent->left == i) { // i is a left child | |||
| i = i->parent; | |||
| } | |||
| i = i->parent; | |||
| } | |||
| return i; | |||
| } | |||
| @@ -0,0 +1,148 @@ | |||
| /* | |||
| Copyright 2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef ZIX_TREE_H | |||
| #define ZIX_TREE_H | |||
| #include <stddef.h> | |||
| #include "zix/common.h" | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /** | |||
| @addtogroup zix | |||
| @{ | |||
| @name Tree | |||
| @{ | |||
| */ | |||
| /** | |||
| A balanced binary search tree. | |||
| */ | |||
| typedef struct ZixTreeImpl ZixTree; | |||
| /** | |||
| An iterator over a @ref ZixTree. | |||
| */ | |||
| typedef struct ZixTreeNodeImpl ZixTreeIter; | |||
| /** | |||
| Create a new (empty) tree. | |||
| */ | |||
| ZIX_API ZixTree* | |||
| zix_tree_new(bool allow_duplicates, | |||
| ZixComparator cmp, | |||
| void* cmp_data, | |||
| ZixDestroyFunc destroy); | |||
| /** | |||
| Free @a t. | |||
| */ | |||
| ZIX_API void | |||
| zix_tree_free(ZixTree* t); | |||
| /** | |||
| Return the number of elements in @a t. | |||
| */ | |||
| ZIX_API size_t | |||
| zix_tree_size(const ZixTree* t); | |||
| /** | |||
| Insert the element @a e into @a t and point @a ti at the new element. | |||
| */ | |||
| ZIX_API ZixStatus | |||
| zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti); | |||
| /** | |||
| Remove the item pointed at by @a ti from @a t. | |||
| */ | |||
| ZIX_API ZixStatus | |||
| zix_tree_remove(ZixTree* t, ZixTreeIter* ti); | |||
| /** | |||
| Set @a ti to an element equal to @a e in @a t. | |||
| If no such item exists, @a ti is set to NULL. | |||
| */ | |||
| ZIX_API ZixStatus | |||
| zix_tree_find(const ZixTree* t, const void* e, ZixTreeIter** ti); | |||
| /** | |||
| Return the data associated with the given tree item. | |||
| */ | |||
| ZIX_API void* | |||
| zix_tree_get(ZixTreeIter* ti); | |||
| /** | |||
| Return an iterator to the first (smallest) element in @a t. | |||
| */ | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_begin(ZixTree* t); | |||
| /** | |||
| Return an iterator the the element one past the last element in @a t. | |||
| */ | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_end(ZixTree* t); | |||
| /** | |||
| Return true iff @a i is an iterator to the end of its tree. | |||
| */ | |||
| ZIX_API bool | |||
| zix_tree_iter_is_end(ZixTreeIter* i); | |||
| /** | |||
| Return an iterator to the last (largest) element in @a t. | |||
| */ | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_rbegin(ZixTree* t); | |||
| /** | |||
| Return an iterator the the element one before the first element in @a t. | |||
| */ | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_rend(ZixTree* t); | |||
| /** | |||
| Return true iff @a i is an iterator to the reverse end of its tree. | |||
| */ | |||
| ZIX_API bool | |||
| zix_tree_iter_is_rend(ZixTreeIter* i); | |||
| /** | |||
| Return an iterator that points to the element one past @a i. | |||
| */ | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_iter_next(ZixTreeIter* i); | |||
| /** | |||
| Return an iterator that points to the element one before @a i. | |||
| */ | |||
| ZIX_API ZixTreeIter* | |||
| zix_tree_iter_prev(ZixTreeIter* i); | |||
| /** | |||
| @} | |||
| @} | |||
| */ | |||
| #ifdef __cplusplus | |||
| } /* extern "C" */ | |||
| #endif | |||
| #endif /* ZIX_TREE_H */ | |||
| @@ -0,0 +1,7 @@ | |||
| @prefix lv2: <http://lv2plug.in/ns/lv2core#> . | |||
| @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
| <http://example.org/lilv-test-plugin> | |||
| a lv2:Plugin ; | |||
| lv2:binary <test_plugin@SHLIB_EXT@> ; | |||
| rdfs:seeAlso <test_plugin.ttl> . | |||
| @@ -0,0 +1,384 @@ | |||
| /* | |||
| Lilv Test Plugin | |||
| Copyright 2011 David Robillard <d@drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <assert.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lv2/lv2plug.in/ns/ext/atom/atom.h" | |||
| #include "lv2/lv2plug.in/ns/ext/state/state.h" | |||
| #include "lv2/lv2plug.in/ns/ext/urid/urid.h" | |||
| #include "lv2/lv2plug.in/ns/lv2core/lv2.h" | |||
| #define TEST_URI "http://example.org/lilv-test-plugin" | |||
| enum { | |||
| TEST_INPUT = 0, | |||
| TEST_OUTPUT = 1 | |||
| }; | |||
| typedef struct { | |||
| LV2_URID_Map* map; | |||
| struct { | |||
| LV2_URID atom_Float; | |||
| } uris; | |||
| char* tmp_file_path; | |||
| char* rec_file_path; | |||
| FILE* rec_file; | |||
| float* input; | |||
| float* output; | |||
| unsigned num_runs; | |||
| } Test; | |||
| static void | |||
| cleanup(LV2_Handle instance) | |||
| { | |||
| Test* test = (Test*)instance; | |||
| if (test->rec_file) { | |||
| fclose(test->rec_file); | |||
| } | |||
| free(test->tmp_file_path); | |||
| free(test->rec_file_path); | |||
| free(instance); | |||
| } | |||
| static void | |||
| connect_port(LV2_Handle instance, | |||
| uint32_t port, | |||
| void* data) | |||
| { | |||
| Test* test = (Test*)instance; | |||
| switch (port) { | |||
| case TEST_INPUT: | |||
| test->input = (float*)data; | |||
| break; | |||
| case TEST_OUTPUT: | |||
| test->output = (float*)data; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| static LV2_Handle | |||
| instantiate(const LV2_Descriptor* descriptor, | |||
| double rate, | |||
| const char* path, | |||
| const LV2_Feature* const* features) | |||
| { | |||
| Test* test = (Test*)malloc(sizeof(Test)); | |||
| if (!test) { | |||
| return NULL; | |||
| } | |||
| test->map = NULL; | |||
| test->input = NULL; | |||
| test->output = NULL; | |||
| test->num_runs = 0; | |||
| test->tmp_file_path = (char*)malloc(L_tmpnam); | |||
| test->rec_file_path = NULL; | |||
| test->rec_file = NULL; | |||
| tmpnam(test->tmp_file_path); | |||
| LV2_State_Make_Path* make_path = NULL; | |||
| for (int i = 0; features[i]; ++i) { | |||
| if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { | |||
| test->map = (LV2_URID_Map*)features[i]->data; | |||
| test->uris.atom_Float = test->map->map( | |||
| test->map->handle, LV2_ATOM__Float); | |||
| } else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { | |||
| make_path = (LV2_State_Make_Path*)features[i]->data; | |||
| } | |||
| } | |||
| if (!test->map) { | |||
| fprintf(stderr, "Host does not support urid:map\n"); | |||
| free(test); | |||
| return NULL; | |||
| } | |||
| if (make_path) { | |||
| test->rec_file_path = make_path->path(make_path->handle, "recfile"); | |||
| if (!(test->rec_file = fopen(test->rec_file_path, "w"))) { | |||
| fprintf(stderr, "ERROR: Failed to open rec file\n"); | |||
| } | |||
| fprintf(test->rec_file, "instantiate\n"); | |||
| } | |||
| return (LV2_Handle)test; | |||
| } | |||
| static void | |||
| run(LV2_Handle instance, | |||
| uint32_t sample_count) | |||
| { | |||
| Test* test = (Test*)instance; | |||
| *test->output = *test->input; | |||
| if (sample_count == 1) { | |||
| ++test->num_runs; | |||
| } else if (sample_count == 2 && test->rec_file) { | |||
| // Append to rec file (changes size) | |||
| fprintf(test->rec_file, "run\n"); | |||
| } else if (sample_count == 3 && test->rec_file) { | |||
| // Change the first byte of rec file (doesn't change size) | |||
| fseek(test->rec_file, 0, SEEK_SET); | |||
| fprintf(test->rec_file, "X"); | |||
| fseek(test->rec_file, 0, SEEK_END); | |||
| } | |||
| } | |||
| static uint32_t | |||
| map_uri(Test* plugin, const char* uri) | |||
| { | |||
| return plugin->map->map(plugin->map->handle, uri); | |||
| } | |||
| static LV2_State_Status | |||
| save(LV2_Handle instance, | |||
| LV2_State_Store_Function store, | |||
| void* callback_data, | |||
| uint32_t flags, | |||
| const LV2_Feature* const* features) | |||
| { | |||
| Test* plugin = (Test*)instance; | |||
| LV2_State_Map_Path* map_path = NULL; | |||
| LV2_State_Make_Path* make_path = NULL; | |||
| for (int i = 0; features && features[i]; ++i) { | |||
| if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { | |||
| map_path = (LV2_State_Map_Path*)features[i]->data; | |||
| } else if (!strcmp(features[i]->URI, LV2_STATE__makePath)) { | |||
| make_path = (LV2_State_Make_Path*)features[i]->data; | |||
| } | |||
| } | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/greeting"), | |||
| "hello", | |||
| strlen("hello") + 1, | |||
| map_uri(plugin, LV2_ATOM__String), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| const uint32_t urid = map_uri(plugin, "http://example.org/urivalue"); | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/uri"), | |||
| &urid, | |||
| sizeof(uint32_t), | |||
| map_uri(plugin, LV2_ATOM__URID), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/num-runs"), | |||
| &plugin->num_runs, | |||
| sizeof(plugin->num_runs), | |||
| map_uri(plugin, LV2_ATOM__Int), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| const float two = 2.0f; | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/two"), | |||
| &two, | |||
| sizeof(two), | |||
| map_uri(plugin, LV2_ATOM__Float), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| const uint32_t affirmative = 1; | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/true"), | |||
| &affirmative, | |||
| sizeof(affirmative), | |||
| map_uri(plugin, LV2_ATOM__Bool), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| const uint32_t negative = 0; | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/false"), | |||
| &negative, | |||
| sizeof(negative), | |||
| map_uri(plugin, LV2_ATOM__Bool), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| const uint8_t blob[] = "I am a blob of arbitrary data."; | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/blob"), | |||
| blob, | |||
| sizeof(blob), | |||
| map_uri(plugin, "http://example.org/SomeUnknownType"), | |||
| LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); | |||
| if (map_path) { | |||
| FILE* file = fopen(plugin->tmp_file_path, "w"); | |||
| fprintf(file, "Hello\n"); | |||
| fclose(file); | |||
| char* apath = map_path->abstract_path(map_path->handle, | |||
| plugin->tmp_file_path); | |||
| char* apath2 = map_path->abstract_path(map_path->handle, | |||
| plugin->tmp_file_path); | |||
| if (strcmp(apath, apath2)) { | |||
| fprintf(stderr, "ERROR: Path %s != %s\n", apath, apath2); | |||
| } | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/extfile"), | |||
| apath, | |||
| strlen(apath) + 1, | |||
| map_uri(plugin, LV2_ATOM__Path), | |||
| LV2_STATE_IS_PORTABLE); | |||
| free(apath); | |||
| free(apath2); | |||
| if (plugin->rec_file) { | |||
| fflush(plugin->rec_file); | |||
| apath = map_path->abstract_path(map_path->handle, | |||
| plugin->rec_file_path); | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/recfile"), | |||
| apath, | |||
| strlen(apath) + 1, | |||
| map_uri(plugin, LV2_ATOM__Path), | |||
| LV2_STATE_IS_PORTABLE); | |||
| free(apath); | |||
| } | |||
| if (make_path) { | |||
| char* spath = make_path->path(make_path->handle, "save"); | |||
| FILE* sfile = fopen(spath, "w"); | |||
| fprintf(sfile, "save"); | |||
| fclose(sfile); | |||
| apath = map_path->abstract_path(map_path->handle, spath); | |||
| store(callback_data, | |||
| map_uri(plugin, "http://example.org/save-file"), | |||
| apath, | |||
| strlen(apath) + 1, | |||
| map_uri(plugin, LV2_ATOM__Path), | |||
| LV2_STATE_IS_PORTABLE); | |||
| free(apath); | |||
| free(spath); | |||
| } | |||
| } | |||
| return LV2_STATE_SUCCESS; | |||
| } | |||
| static LV2_State_Status | |||
| restore(LV2_Handle instance, | |||
| LV2_State_Retrieve_Function retrieve, | |||
| void* callback_data, | |||
| uint32_t flags, | |||
| const LV2_Feature* const* features) | |||
| { | |||
| Test* plugin = (Test*)instance; | |||
| LV2_State_Map_Path* map_path = NULL; | |||
| for (int i = 0; features && features[i]; ++i) { | |||
| if (!strcmp(features[i]->URI, LV2_STATE__mapPath)) { | |||
| map_path = (LV2_State_Map_Path*)features[i]->data; | |||
| } | |||
| } | |||
| size_t size; | |||
| uint32_t type; | |||
| uint32_t valflags; | |||
| plugin->num_runs = *(int32_t*)retrieve( | |||
| callback_data, | |||
| map_uri(plugin, "http://example.org/num-runs"), | |||
| &size, &type, &valflags); | |||
| if (!map_path) { | |||
| return LV2_STATE_ERR_NO_FEATURE; | |||
| } | |||
| char* apath = (char*)retrieve( | |||
| callback_data, | |||
| map_uri(plugin, "http://example.org/extfile"), | |||
| &size, &type, &valflags); | |||
| if (apath) { | |||
| char* path = map_path->absolute_path(map_path->handle, apath); | |||
| FILE* f = fopen(path, "r"); | |||
| char str[8]; | |||
| size_t n_read = fread(str, 1, sizeof(str), f); | |||
| fclose(f); | |||
| if (strncmp(str, "Hello\n", n_read)) { | |||
| fprintf(stderr, "error: Restored bad file contents `%s' != `Hello'\n", | |||
| str); | |||
| } | |||
| free(path); | |||
| } | |||
| apath = (char*)retrieve( | |||
| callback_data, | |||
| map_uri(plugin, "http://example.org/save-file"), | |||
| &size, &type, &valflags); | |||
| if (apath) { | |||
| char* spath = map_path->absolute_path(map_path->handle, apath); | |||
| FILE* sfile = fopen(spath, "r"); | |||
| if (!sfile) { | |||
| fprintf(stderr, "error: Failed to open save file %s\n", spath); | |||
| } else { | |||
| fclose(sfile); | |||
| } | |||
| free(spath); | |||
| } else { | |||
| fprintf(stderr, "error: Failed to restore save file.\n"); | |||
| } | |||
| return LV2_STATE_SUCCESS; | |||
| } | |||
| static const void* | |||
| extension_data(const char* uri) | |||
| { | |||
| static const LV2_State_Interface state = { save, restore }; | |||
| if (!strcmp(uri, LV2_STATE__interface)) { | |||
| return &state; | |||
| } | |||
| return NULL; | |||
| } | |||
| static const LV2_Descriptor descriptor = { | |||
| TEST_URI, | |||
| instantiate, | |||
| connect_port, | |||
| NULL, // activate, | |||
| run, | |||
| NULL, // deactivate, | |||
| cleanup, | |||
| extension_data | |||
| }; | |||
| LV2_SYMBOL_EXPORT | |||
| const LV2_Descriptor* lv2_descriptor(uint32_t index) | |||
| { | |||
| switch (index) { | |||
| case 0: | |||
| return &descriptor; | |||
| default: | |||
| return NULL; | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| # Lilv Test Plugin | |||
| # Copyright 2011 David Robillard <d@drobilla.net> | |||
| # | |||
| # Permission to use, copy, modify, and/or distribute this software for any | |||
| # purpose with or without fee is hereby granted, provided that the above | |||
| # copyright notice and this permission notice appear in all copies. | |||
| # | |||
| # THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| @prefix doap: <http://usefulinc.com/ns/doap#> . | |||
| @prefix foaf: <http://xmlns.com/foaf/0.1/> . | |||
| @prefix lv2: <http://lv2plug.in/ns/lv2core#> . | |||
| @prefix ui: <http://lv2plug.in/ns/extensions/ui#> . | |||
| <http://example.org/lilv-test-plugin> | |||
| a lv2:Plugin ; | |||
| doap:name "Lilv Test" ; | |||
| doap:license <http://opensource.org/licenses/isc> ; | |||
| lv2:requiredFeature <http://lv2plug.in/ns/ext/urid#Mapper> ; | |||
| lv2:optionalFeature lv2:hardRTCapable ; | |||
| lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ; | |||
| lv2:port [ | |||
| a lv2:InputPort , | |||
| lv2:ControlPort ; | |||
| lv2:index 0 ; | |||
| lv2:symbol "input" ; | |||
| lv2:name "Input" | |||
| ] , [ | |||
| a lv2:OutputPort , | |||
| lv2:ControlPort ; | |||
| lv2:index 1 ; | |||
| lv2:symbol "output" ; | |||
| lv2:name "Output" | |||
| ] . | |||
| @@ -0,0 +1,52 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| /** | |||
| @file bench.h A simple real-time benchmarking API. | |||
| */ | |||
| #ifndef BENCH_H | |||
| #define BENCH_H | |||
| #define _POSIX_C_SOURCE 199309L | |||
| #include <time.h> | |||
| #include <sys/time.h> | |||
| static inline double | |||
| bench_elapsed_s(const struct timespec* start, const struct timespec* end) | |||
| { | |||
| return ((end->tv_sec - start->tv_sec) | |||
| + ((end->tv_nsec - start->tv_nsec) * 0.000000001)); | |||
| } | |||
| static inline struct timespec | |||
| bench_start() | |||
| { | |||
| struct timespec start_t; | |||
| clock_gettime(CLOCK_REALTIME, &start_t); | |||
| return start_t; | |||
| } | |||
| static inline double | |||
| bench_end(const struct timespec* start_t) | |||
| { | |||
| struct timespec end_t; | |||
| clock_gettime(CLOCK_REALTIME, &end_t); | |||
| return bench_elapsed_s(start_t, &end_t); | |||
| } | |||
| #endif /* BENCH_H */ | |||
| @@ -0,0 +1,38 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <stdio.h> | |||
| #include "lilv/lilv.h" | |||
| #include "lilv_config.h" | |||
| int | |||
| main(int argc, char** argv) | |||
| { | |||
| LilvWorld* world = lilv_world_new(); | |||
| lilv_world_load_all(world); | |||
| const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
| LILV_FOREACH(plugins, p, plugins) { | |||
| const LilvPlugin* plugin = lilv_plugins_get(plugins, p); | |||
| lilv_plugin_get_class(plugin); | |||
| } | |||
| lilv_world_free(world); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| # Bash auto-completion script written for lv2info and lv2jack. | |||
| # Could be adapted to any other program that takes an | |||
| # LV2 plugin URI as parameter. | |||
| # Updated for Lilv by David Robillard <d@drobilla.net> on 2012-01-08. | |||
| # Written by Lars Luthman <lars.luthman@gmail.com> on 2009-10-12. | |||
| # No copyright claimed for this script. Do what you want with it. | |||
| # For some reason Bash splits the command line not only at whitespace | |||
| # but also at ':' signs before putting the parts into COMP_WORDS. | |||
| # Since ':' is used in all URIs, which are what we want to complete, | |||
| # we have to put the URI back together before we can complete it | |||
| # and then cut off the parts we prepended from the completions. | |||
| # It probably breaks in some special cases but for most common uses | |||
| # it should work fine. | |||
| function _lv2info() { | |||
| local uri cur opts w wn raw_reply len type | |||
| opts=`lv2ls | xargs -n1 echo -n " "` | |||
| # This is the last "word", as split by Bash. | |||
| cur="${COMP_WORDS[COMP_CWORD]}" | |||
| w="$cur" | |||
| # Add the previous word while it or this one is a word break character | |||
| for i in `seq $(( $COMP_CWORD - 1 )) -1 1`; do | |||
| wn="${COMP_WORDS[i]}" | |||
| if expr "$COMP_WORDBREAKS" : ".*$wn" > /dev/null; then | |||
| if expr "$COMP_WORDBREAKS" : ".*$w" > /dev/null; then | |||
| break | |||
| fi | |||
| fi | |||
| w="$wn" | |||
| uri="$w$uri" | |||
| done | |||
| # Check the length of the words we prepend | |||
| len=${#uri} | |||
| uri="$uri$cur" | |||
| raw_reply="$(compgen -W "${opts}" -- ${uri})" | |||
| # If we are listing alternatives, just print the full URIs. | |||
| type=`echo $COMP_TYPE | awk '{ printf "%c", $1 }'` | |||
| if expr "?!@%" : ".*$type" > /dev/null; then | |||
| COMPREPLY=( $raw_reply ) | |||
| return 0 | |||
| fi | |||
| # Otherwise, strip the prepended words from all completion suggestions. | |||
| COMPREPLY=() | |||
| for i in $raw_reply; do | |||
| COMPREPLY=( ${COMPREPLY[@]} ${i:len} ) | |||
| done | |||
| } | |||
| complete -F _lv2info lv2info | |||
| # And the same for lv2jack. | |||
| complete -F _lv2info lv2jack | |||
| @@ -0,0 +1,226 @@ | |||
| /* | |||
| Copyright 2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #define _POSIX_C_SOURCE 199309L | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lilv/lilv.h" | |||
| #include "lv2/lv2plug.in/ns/ext/atom/atom.h" | |||
| #include "lilv_config.h" | |||
| #include "bench.h" | |||
| #include "uri_table.h" | |||
| static LilvNode* atom_AtomPort = NULL; | |||
| static LilvNode* atom_Sequence = NULL; | |||
| static LilvNode* lv2_AudioPort = NULL; | |||
| static LilvNode* lv2_CVPort = NULL; | |||
| static LilvNode* lv2_ControlPort = NULL; | |||
| static LilvNode* lv2_InputPort = NULL; | |||
| static LilvNode* lv2_OutputPort = NULL; | |||
| static LilvNode* urid_map = NULL; | |||
| static bool full_output = false; | |||
| static void | |||
| print_version(void) | |||
| { | |||
| printf( | |||
| "lv2bench (lilv) " LILV_VERSION "\n" | |||
| "Copyright 2012 David Robillard <http://drobilla.net>\n" | |||
| "License: <http://www.opensource.org/licenses/isc-license>\n" | |||
| "This is free software: you are free to change and redistribute it.\n" | |||
| "There is NO WARRANTY, to the extent permitted by law.\n"); | |||
| } | |||
| static void | |||
| print_usage(void) | |||
| { | |||
| printf("lv2bench - Benchmark all installed and supported LV2 plugins.\n"); | |||
| printf("Usage: lv2bench [OPTIONS]\n"); | |||
| printf("\n"); | |||
| printf(" -b BLOCK_SIZE Specify block size, in audio frames.\n"); | |||
| printf(" -f, --full Full plottable output.\n"); | |||
| printf(" -h, --help Display this help and exit.\n"); | |||
| printf(" -n FRAMES Total number of audio frames to process\n"); | |||
| printf(" --version Display version information and exit\n"); | |||
| } | |||
| static double | |||
| bench(const LilvPlugin* p, uint32_t sample_count, uint32_t block_size) | |||
| { | |||
| URITable uri_table; | |||
| uri_table_init(&uri_table); | |||
| LV2_URID_Map map = { &uri_table, uri_table_map }; | |||
| LV2_Feature map_feature = { LV2_URID_MAP_URI, &map }; | |||
| LV2_URID_Unmap unmap = { &uri_table, uri_table_unmap }; | |||
| LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap }; | |||
| const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL }; | |||
| float* const buf = (float*)calloc(block_size * 2, sizeof(float)); | |||
| float* const in = buf; | |||
| float* const out = buf + block_size; | |||
| if (!buf) { | |||
| fprintf(stderr, "Out of memory\n"); | |||
| return 0.0; | |||
| } | |||
| LV2_Atom_Sequence seq = { | |||
| { sizeof(LV2_Atom_Sequence_Body), | |||
| uri_table_map(&uri_table, LV2_ATOM__Sequence) }, | |||
| { 0, 0 } }; | |||
| const char* uri = lilv_node_as_string(lilv_plugin_get_uri(p)); | |||
| LilvNodes* required = lilv_plugin_get_required_features(p); | |||
| LILV_FOREACH(nodes, i, required) { | |||
| const LilvNode* feature = lilv_nodes_get(required, i); | |||
| if (!lilv_node_equals(feature, urid_map)) { | |||
| fprintf(stderr, "<%s> requires feature <%s>, skipping\n", | |||
| uri, lilv_node_as_uri(feature)); | |||
| free(buf); | |||
| return 0.0; | |||
| } | |||
| } | |||
| LilvInstance* instance = lilv_plugin_instantiate(p, 48000.0, features); | |||
| if (!instance) { | |||
| fprintf(stderr, "Failed to instantiate <%s>\n", | |||
| lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
| free(buf); | |||
| return 0.0; | |||
| } | |||
| float* controls = (float*)calloc( | |||
| lilv_plugin_get_num_ports(p), sizeof(float)); | |||
| lilv_plugin_get_port_ranges_float(p, NULL, NULL, controls); | |||
| const uint32_t n_ports = lilv_plugin_get_num_ports(p); | |||
| for (uint32_t index = 0; index < n_ports; ++index) { | |||
| const LilvPort* port = lilv_plugin_get_port_by_index(p, index); | |||
| if (lilv_port_is_a(p, port, lv2_ControlPort)) { | |||
| lilv_instance_connect_port(instance, index, &controls[index]); | |||
| } else if (lilv_port_is_a(p, port, lv2_AudioPort) || | |||
| lilv_port_is_a(p, port, lv2_CVPort)) { | |||
| if (lilv_port_is_a(p, port, lv2_InputPort)) { | |||
| lilv_instance_connect_port(instance, index, in); | |||
| } else if (lilv_port_is_a(p, port, lv2_OutputPort)) { | |||
| lilv_instance_connect_port(instance, index, out); | |||
| } else { | |||
| fprintf(stderr, "<%s> port %d neither input nor output, skipping\n", | |||
| uri, index); | |||
| lilv_instance_free(instance); | |||
| free(buf); | |||
| free(controls); | |||
| return 0.0; | |||
| } | |||
| } else if (lilv_port_is_a(p, port, atom_AtomPort)) { | |||
| lilv_instance_connect_port(instance, index, &seq); | |||
| } else { | |||
| fprintf(stderr, "<%s> port %d has unknown type, skipping\n", | |||
| uri, index); | |||
| lilv_instance_free(instance); | |||
| free(buf); | |||
| free(controls); | |||
| return 0.0; | |||
| } | |||
| } | |||
| lilv_instance_activate(instance); | |||
| struct timespec ts = bench_start(); | |||
| for (uint32_t i = 0; i < (sample_count / block_size); ++i) { | |||
| lilv_instance_run(instance, block_size); | |||
| } | |||
| const double elapsed = bench_end(&ts); | |||
| lilv_instance_deactivate(instance); | |||
| lilv_instance_free(instance); | |||
| uri_table_destroy(&uri_table); | |||
| if (full_output) { | |||
| printf("%d %d ", block_size, sample_count); | |||
| } | |||
| printf("%lf %s\n", elapsed, uri); | |||
| free(buf); | |||
| free(controls); | |||
| return elapsed; | |||
| } | |||
| int | |||
| main(int argc, char** argv) | |||
| { | |||
| uint32_t block_size = 512; | |||
| uint32_t sample_count = (1 << 19); | |||
| for (int i = 1; i < argc; ++i) { | |||
| if (!strcmp(argv[i], "--version")) { | |||
| print_version(); | |||
| return 0; | |||
| } else if (!strcmp(argv[i], "--help")) { | |||
| print_usage(); | |||
| return 0; | |||
| } else if (!strcmp(argv[i], "-f")) { | |||
| full_output = true; | |||
| } else if (!strcmp(argv[i], "-n") && (i + 1 < argc)) { | |||
| sample_count = atoi(argv[++i]); | |||
| } else if (!strcmp(argv[i], "-b") && (i + 1 < argc)) { | |||
| block_size = atoi(argv[++i]); | |||
| } else { | |||
| print_usage(); | |||
| return 1; | |||
| } | |||
| } | |||
| LilvWorld* world = lilv_world_new(); | |||
| lilv_world_load_all(world); | |||
| atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); | |||
| atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence); | |||
| lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort); | |||
| lv2_CVPort = lilv_new_uri(world, LV2_CORE__CVPort); | |||
| lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort); | |||
| lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort); | |||
| lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort); | |||
| urid_map = lilv_new_uri(world, LV2_URID__map); | |||
| if (full_output) { | |||
| printf("# Block Samples Time Plugin\n"); | |||
| } | |||
| const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
| LILV_FOREACH(plugins, i, plugins) { | |||
| bench(lilv_plugins_get(plugins, i), sample_count, block_size); | |||
| } | |||
| lilv_node_free(urid_map); | |||
| lilv_node_free(lv2_OutputPort); | |||
| lilv_node_free(lv2_InputPort); | |||
| lilv_node_free(lv2_ControlPort); | |||
| lilv_node_free(lv2_CVPort); | |||
| lilv_node_free(lv2_AudioPort); | |||
| lilv_node_free(atom_Sequence); | |||
| lilv_node_free(atom_AtomPort); | |||
| lilv_world_free(world); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,437 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <float.h> | |||
| #include <math.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" | |||
| #include "lv2/lv2plug.in/ns/ext/presets/presets.h" | |||
| #include "lv2/lv2plug.in/ns/ext/event/event.h" | |||
| #include "lilv/lilv.h" | |||
| #include "lilv_config.h" | |||
| #ifdef _MSC_VER | |||
| # define isnan _isnan | |||
| #endif | |||
| LilvNode* applies_to_pred = NULL; | |||
| LilvNode* control_class = NULL; | |||
| LilvNode* event_class = NULL; | |||
| LilvNode* group_pred = NULL; | |||
| LilvNode* label_pred = NULL; | |||
| LilvNode* preset_class = NULL; | |||
| LilvNode* designation_pred = NULL; | |||
| LilvNode* supports_event_pred = NULL; | |||
| static void | |||
| print_port(const LilvPlugin* p, | |||
| uint32_t index, | |||
| float* mins, | |||
| float* maxes, | |||
| float* defaults) | |||
| { | |||
| const LilvPort* port = lilv_plugin_get_port_by_index(p, index); | |||
| printf("\n\tPort %d:\n", index); | |||
| if (!port) { | |||
| printf("\t\tERROR: Illegal/nonexistent port\n"); | |||
| return; | |||
| } | |||
| bool first = true; | |||
| const LilvNodes* classes = lilv_port_get_classes(p, port); | |||
| printf("\t\tType: "); | |||
| LILV_FOREACH(nodes, i, classes) { | |||
| const LilvNode* value = lilv_nodes_get(classes, i); | |||
| if (!first) { | |||
| printf("\n\t\t "); | |||
| } | |||
| printf("%s", lilv_node_as_uri(value)); | |||
| first = false; | |||
| } | |||
| if (lilv_port_is_a(p, port, event_class)) { | |||
| LilvNodes* supported = lilv_port_get_value( | |||
| p, port, supports_event_pred); | |||
| if (lilv_nodes_size(supported) > 0) { | |||
| printf("\n\t\tSupported events:\n"); | |||
| LILV_FOREACH(nodes, i, supported) { | |||
| const LilvNode* value = lilv_nodes_get(supported, i); | |||
| printf("\t\t\t%s\n", lilv_node_as_uri(value)); | |||
| } | |||
| } | |||
| lilv_nodes_free(supported); | |||
| } | |||
| LilvScalePoints* points = lilv_port_get_scale_points(p, port); | |||
| if (points) | |||
| printf("\n\t\tScale Points:\n"); | |||
| LILV_FOREACH(scale_points, i, points) { | |||
| const LilvScalePoint* point = lilv_scale_points_get(points, i); | |||
| printf("\t\t\t%s = \"%s\"\n", | |||
| lilv_node_as_string(lilv_scale_point_get_value(point)), | |||
| lilv_node_as_string(lilv_scale_point_get_label(point))); | |||
| } | |||
| lilv_scale_points_free(points); | |||
| const LilvNode* sym = lilv_port_get_symbol(p, port); | |||
| printf("\n\t\tSymbol: %s\n", lilv_node_as_string(sym)); | |||
| LilvNode* name = lilv_port_get_name(p, port); | |||
| printf("\t\tName: %s\n", lilv_node_as_string(name)); | |||
| lilv_node_free(name); | |||
| LilvNodes* groups = lilv_port_get_value(p, port, group_pred); | |||
| if (lilv_nodes_size(groups) > 0) { | |||
| printf("\t\tGroup: %s\n", | |||
| lilv_node_as_string(lilv_nodes_get_first(groups))); | |||
| } | |||
| lilv_nodes_free(groups); | |||
| LilvNodes* designations = lilv_port_get_value(p, port, designation_pred); | |||
| if (lilv_nodes_size(designations) > 0) { | |||
| printf("\t\tDesignation: %s\n", | |||
| lilv_node_as_string(lilv_nodes_get_first(designations))); | |||
| } | |||
| lilv_nodes_free(designations); | |||
| if (lilv_port_is_a(p, port, control_class)) { | |||
| if (!isnan(mins[index])) | |||
| printf("\t\tMinimum: %f\n", mins[index]); | |||
| if (!isnan(mins[index])) | |||
| printf("\t\tMaximum: %f\n", maxes[index]); | |||
| if (!isnan(mins[index])) | |||
| printf("\t\tDefault: %f\n", defaults[index]); | |||
| } | |||
| LilvNodes* properties = lilv_port_get_properties(p, port); | |||
| if (lilv_nodes_size(properties) > 0) | |||
| printf("\t\tProperties: "); | |||
| first = true; | |||
| LILV_FOREACH(nodes, i, properties) { | |||
| if (!first) { | |||
| printf("\t\t "); | |||
| } | |||
| printf("%s\n", lilv_node_as_uri(lilv_nodes_get(properties, i))); | |||
| first = false; | |||
| } | |||
| if (lilv_nodes_size(properties) > 0) | |||
| printf("\n"); | |||
| lilv_nodes_free(properties); | |||
| } | |||
| static void | |||
| print_plugin(LilvWorld* world, | |||
| const LilvPlugin* p) | |||
| { | |||
| LilvNode* val = NULL; | |||
| printf("%s\n\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
| val = lilv_plugin_get_name(p); | |||
| if (val) { | |||
| printf("\tName: %s\n", lilv_node_as_string(val)); | |||
| lilv_node_free(val); | |||
| } | |||
| const LilvPluginClass* pclass = lilv_plugin_get_class(p); | |||
| const LilvNode* class_label = lilv_plugin_class_get_label(pclass); | |||
| if (class_label) { | |||
| printf("\tClass: %s\n", lilv_node_as_string(class_label)); | |||
| } | |||
| val = lilv_plugin_get_author_name(p); | |||
| if (val) { | |||
| printf("\tAuthor: %s\n", lilv_node_as_string(val)); | |||
| lilv_node_free(val); | |||
| } | |||
| val = lilv_plugin_get_author_email(p); | |||
| if (val) { | |||
| printf("\tAuthor Email: %s\n", lilv_node_as_uri(val)); | |||
| lilv_node_free(val); | |||
| } | |||
| val = lilv_plugin_get_author_homepage(p); | |||
| if (val) { | |||
| printf("\tAuthor Homepage: %s\n", lilv_node_as_uri(val)); | |||
| lilv_node_free(val); | |||
| } | |||
| if (lilv_plugin_has_latency(p)) { | |||
| uint32_t latency_port = lilv_plugin_get_latency_port_index(p); | |||
| printf("\tHas latency: yes, reported by port %d\n", latency_port); | |||
| } else { | |||
| printf("\tHas latency: no\n"); | |||
| } | |||
| printf("\tBundle: %s\n", | |||
| lilv_node_as_uri(lilv_plugin_get_bundle_uri(p))); | |||
| const LilvNode* binary_uri = lilv_plugin_get_library_uri(p); | |||
| if (binary_uri) { | |||
| printf("\tBinary: %s\n", | |||
| lilv_node_as_uri(lilv_plugin_get_library_uri(p))); | |||
| } | |||
| LilvUIs* uis = lilv_plugin_get_uis(p); | |||
| if (lilv_nodes_size(uis) > 0) { | |||
| printf("\tUIs:\n"); | |||
| LILV_FOREACH(uis, i, uis) { | |||
| const LilvUI* ui = lilv_uis_get(uis, i); | |||
| printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); | |||
| const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); | |||
| const LilvNodes* types = lilv_ui_get_classes(ui); | |||
| LILV_FOREACH(nodes, t, types) { | |||
| printf("\t\t\tClass: %s\n", | |||
| lilv_node_as_uri(lilv_nodes_get(types, t))); | |||
| } | |||
| if (binary) | |||
| printf("\t\t\tBinary: %s\n", binary); | |||
| printf("\t\t\tBundle: %s\n", | |||
| lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); | |||
| } | |||
| } | |||
| lilv_uis_free(uis); | |||
| printf("\tData URIs: "); | |||
| const LilvNodes* data_uris = lilv_plugin_get_data_uris(p); | |||
| bool first = true; | |||
| LILV_FOREACH(nodes, i, data_uris) { | |||
| if (!first) { | |||
| printf("\n\t "); | |||
| } | |||
| printf("%s", lilv_node_as_uri(lilv_nodes_get(data_uris, i))); | |||
| first = false; | |||
| } | |||
| printf("\n"); | |||
| /* Required Features */ | |||
| LilvNodes* features = lilv_plugin_get_required_features(p); | |||
| if (features) | |||
| printf("\tRequired Features: "); | |||
| first = true; | |||
| LILV_FOREACH(nodes, i, features) { | |||
| if (!first) { | |||
| printf("\n\t "); | |||
| } | |||
| printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); | |||
| first = false; | |||
| } | |||
| if (features) | |||
| printf("\n"); | |||
| lilv_nodes_free(features); | |||
| /* Optional Features */ | |||
| features = lilv_plugin_get_optional_features(p); | |||
| if (features) | |||
| printf("\tOptional Features: "); | |||
| first = true; | |||
| LILV_FOREACH(nodes, i, features) { | |||
| if (!first) { | |||
| printf("\n\t "); | |||
| } | |||
| printf("%s", lilv_node_as_uri(lilv_nodes_get(features, i))); | |||
| first = false; | |||
| } | |||
| if (features) | |||
| printf("\n"); | |||
| lilv_nodes_free(features); | |||
| /* Extension Data */ | |||
| LilvNodes* data = lilv_plugin_get_extension_data(p); | |||
| if (data) | |||
| printf("\tExtension Data: "); | |||
| first = true; | |||
| LILV_FOREACH(nodes, i, data) { | |||
| if (!first) { | |||
| printf("\n\t "); | |||
| } | |||
| printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); | |||
| first = false; | |||
| } | |||
| if (data) | |||
| printf("\n"); | |||
| lilv_nodes_free(data); | |||
| /* Presets */ | |||
| LilvNodes* presets = lilv_plugin_get_related(p, preset_class); | |||
| if (presets) | |||
| printf("\tPresets: \n"); | |||
| LILV_FOREACH(nodes, i, presets) { | |||
| const LilvNode* preset = lilv_nodes_get(presets, i); | |||
| lilv_world_load_resource(world, preset); | |||
| LilvNodes* titles = lilv_world_find_nodes( | |||
| world, preset, label_pred, NULL); | |||
| if (titles) { | |||
| const LilvNode* title = lilv_nodes_get_first(titles); | |||
| printf("\t %s\n", lilv_node_as_string(title)); | |||
| lilv_nodes_free(titles); | |||
| } else { | |||
| fprintf(stderr, "Preset <%s> has no rdfs:label\n", | |||
| lilv_node_as_string(lilv_nodes_get(presets, i))); | |||
| } | |||
| } | |||
| lilv_nodes_free(presets); | |||
| /* Ports */ | |||
| const uint32_t num_ports = lilv_plugin_get_num_ports(p); | |||
| float* mins = (float*)calloc(num_ports, sizeof(float)); | |||
| float* maxes = (float*)calloc(num_ports, sizeof(float)); | |||
| float* defaults = (float*)calloc(num_ports, sizeof(float)); | |||
| lilv_plugin_get_port_ranges_float(p, mins, maxes, defaults); | |||
| for (uint32_t i = 0; i < num_ports; ++i) | |||
| print_port(p, i, mins, maxes, defaults); | |||
| free(mins); | |||
| free(maxes); | |||
| free(defaults); | |||
| } | |||
| static void | |||
| print_version(void) | |||
| { | |||
| printf( | |||
| "lv2info (lilv) " LILV_VERSION "\n" | |||
| "Copyright 2007-2011 David Robillard <http://drobilla.net>\n" | |||
| "License: <http://www.opensource.org/licenses/isc-license>\n" | |||
| "This is free software: you are free to change and redistribute it.\n" | |||
| "There is NO WARRANTY, to the extent permitted by law.\n"); | |||
| } | |||
| static void | |||
| print_usage(void) | |||
| { | |||
| printf( | |||
| "Usage: lv2info [OPTION]... PLUGIN_URI\n" | |||
| "Print information about an installed LV2 plugin.\n\n" | |||
| " -p FILE Write Turtle description of plugin to FILE\n" | |||
| " -m FILE Add record of plugin to manifest FILE\n" | |||
| " --help Display this help and exit\n" | |||
| " --version Display version information and exit\n\n" | |||
| "For -p and -m, Turtle files are appended to (not overwritten),\n" | |||
| "and @prefix directives are only written if the file was empty.\n" | |||
| "This allows several plugins to be added to a single file.\n"); | |||
| } | |||
| int | |||
| main(int argc, char** argv) | |||
| { | |||
| if (argc == 1) { | |||
| print_usage(); | |||
| return 1; | |||
| } | |||
| const char* plugin_file = NULL; | |||
| const char* manifest_file = NULL; | |||
| const char* plugin_uri = NULL; | |||
| for (int i = 1; i < argc; ++i) { | |||
| if (!strcmp(argv[i], "--version")) { | |||
| print_version(); | |||
| return 0; | |||
| } else if (!strcmp(argv[i], "--help")) { | |||
| print_usage(); | |||
| return 0; | |||
| } else if (!strcmp(argv[i], "-p")) { | |||
| plugin_file = argv[++i]; | |||
| } else if (!strcmp(argv[i], "-m")) { | |||
| manifest_file = argv[++i]; | |||
| } else if (argv[i][0] == '-') { | |||
| print_usage(); | |||
| return 1; | |||
| } else if (i == argc - 1) { | |||
| plugin_uri = argv[i]; | |||
| } | |||
| } | |||
| int ret = 0; | |||
| LilvWorld* world = lilv_world_new(); | |||
| lilv_world_load_all(world); | |||
| LilvNode* uri = lilv_new_uri(world, plugin_uri); | |||
| if (!uri) { | |||
| fprintf(stderr, "Invalid plugin URI\n"); | |||
| lilv_world_free(world); | |||
| return 1; | |||
| } | |||
| applies_to_pred = lilv_new_uri(world, LV2_CORE__appliesTo); | |||
| control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); | |||
| event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); | |||
| group_pred = lilv_new_uri(world, LV2_PORT_GROUPS__group); | |||
| label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); | |||
| preset_class = lilv_new_uri(world, LV2_PRESETS__Preset); | |||
| designation_pred = lilv_new_uri(world, LV2_CORE__designation); | |||
| supports_event_pred = lilv_new_uri(world, LV2_EVENT__supportsEvent); | |||
| const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
| const LilvPlugin* p = lilv_plugins_get_by_uri(plugins, uri); | |||
| if (p && plugin_file) { | |||
| LilvNode* base = lilv_new_uri(world, plugin_file); | |||
| FILE* plugin_fd = fopen(plugin_file, "a"); | |||
| lilv_plugin_write_description(world, p, base, plugin_fd); | |||
| fclose(plugin_fd); | |||
| if (manifest_file) { | |||
| FILE* manifest_fd = fopen(manifest_file, "a"); | |||
| lilv_plugin_write_manifest_entry( | |||
| world, p, base, manifest_fd, plugin_file); | |||
| fclose(manifest_fd); | |||
| } | |||
| lilv_node_free(base); | |||
| } else if (p) { | |||
| print_plugin(world, p); | |||
| } else { | |||
| fprintf(stderr, "Plugin not found.\n"); | |||
| } | |||
| ret = (p != NULL ? 0 : -1); | |||
| lilv_node_free(uri); | |||
| lilv_node_free(supports_event_pred); | |||
| lilv_node_free(designation_pred); | |||
| lilv_node_free(preset_class); | |||
| lilv_node_free(label_pred); | |||
| lilv_node_free(group_pred); | |||
| lilv_node_free(event_class); | |||
| lilv_node_free(control_class); | |||
| lilv_node_free(applies_to_pred); | |||
| lilv_world_free(world); | |||
| return ret; | |||
| } | |||
| @@ -0,0 +1,93 @@ | |||
| /* | |||
| Copyright 2007-2011 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include "lilv/lilv.h" | |||
| #include "lilv_config.h" | |||
| static void | |||
| list_plugins(const LilvPlugins* list, bool show_names) | |||
| { | |||
| LILV_FOREACH(plugins, i, list) { | |||
| const LilvPlugin* p = lilv_plugins_get(list, i); | |||
| if (show_names) { | |||
| LilvNode* n = lilv_plugin_get_name(p); | |||
| printf("%s\n", lilv_node_as_string(n)); | |||
| lilv_node_free(n); | |||
| } else { | |||
| printf("%s\n", lilv_node_as_uri(lilv_plugin_get_uri(p))); | |||
| } | |||
| } | |||
| } | |||
| static void | |||
| print_version(void) | |||
| { | |||
| printf( | |||
| "lv2ls (lilv) " LILV_VERSION "\n" | |||
| "Copyright 2007-2011 David Robillard <http://drobilla.net>\n" | |||
| "License: <http://www.opensource.org/licenses/isc-license>\n" | |||
| "This is free software: you are free to change and redistribute it.\n" | |||
| "There is NO WARRANTY, to the extent permitted by law.\n"); | |||
| } | |||
| static void | |||
| print_usage(void) | |||
| { | |||
| printf("Usage: lv2ls [OPTION]...\n"); | |||
| printf("List all installed LV2 plugins.\n"); | |||
| printf("\n"); | |||
| printf(" -n, --names Show names instead of URIs\n"); | |||
| printf(" --help Display this help and exit\n"); | |||
| printf(" --version Display version information and exit\n"); | |||
| printf("\n"); | |||
| printf("The environment variable LV2_PATH can be used to control where\n"); | |||
| printf("this (and all other lilv based LV2 hosts) will search for plugins.\n"); | |||
| } | |||
| int | |||
| main(int argc, char** argv) | |||
| { | |||
| bool show_names = false; | |||
| for (int i = 1; i < argc; ++i) { | |||
| if (!strcmp(argv[i], "--names") || !strcmp(argv[i], "-n")) { | |||
| show_names = true; | |||
| } else if (!strcmp(argv[i], "--version")) { | |||
| print_version(); | |||
| return 0; | |||
| } else if (!strcmp(argv[i], "--help")) { | |||
| print_usage(); | |||
| return 0; | |||
| } else { | |||
| print_usage(); | |||
| return 1; | |||
| } | |||
| } | |||
| LilvWorld* world = lilv_world_new(); | |||
| lilv_world_load_all(world); | |||
| const LilvPlugins* plugins = lilv_world_get_all_plugins(world); | |||
| list_plugins(plugins, show_names); | |||
| lilv_world_free(world); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| /** | |||
| @file uri_table.h A toy URI map/unmap implementation. | |||
| This file contains function definitions and must only be included once. | |||
| */ | |||
| #ifndef URI_TABLE_H | |||
| #define URI_TABLE_H | |||
| typedef struct { | |||
| char** uris; | |||
| size_t n_uris; | |||
| } URITable; | |||
| static void | |||
| uri_table_init(URITable* table) | |||
| { | |||
| table->uris = NULL; | |||
| table->n_uris = 0; | |||
| } | |||
| static void | |||
| uri_table_destroy(URITable* table) | |||
| { | |||
| free(table->uris); | |||
| } | |||
| static LV2_URID | |||
| uri_table_map(LV2_URID_Map_Handle handle, | |||
| const char* uri) | |||
| { | |||
| URITable* table = (URITable*)handle; | |||
| for (size_t i = 0; i < table->n_uris; ++i) { | |||
| if (!strcmp(table->uris[i], uri)) { | |||
| return i + 1; | |||
| } | |||
| } | |||
| const size_t len = strlen(uri); | |||
| table->uris = (char**)realloc(table->uris, ++table->n_uris * sizeof(char*)); | |||
| table->uris[table->n_uris - 1] = malloc(len + 1); | |||
| memcpy(table->uris[table->n_uris - 1], uri, len + 1); | |||
| return table->n_uris; | |||
| } | |||
| static const char* | |||
| uri_table_unmap(LV2_URID_Map_Handle handle, | |||
| LV2_URID urid) | |||
| { | |||
| URITable* table = (URITable*)handle; | |||
| if (urid > 0 && urid <= table->n_uris) { | |||
| return table->uris[urid - 1]; | |||
| } | |||
| return NULL; | |||
| } | |||
| #endif /* URI_TABLE_H */ | |||
| @@ -0,0 +1,378 @@ | |||
| #!/usr/bin/env python | |||
| import os | |||
| import sys | |||
| import subprocess | |||
| from waflib.extras import autowaf as autowaf | |||
| import waflib.Options as Options | |||
| # Version of this package (even if built as a child) | |||
| LILV_VERSION = '0.14.4' | |||
| LILV_MAJOR_VERSION = '0' | |||
| # Library version (UNIX style major, minor, micro) | |||
| # major increment <=> incompatible changes | |||
| # minor increment <=> compatible changes (additions) | |||
| # micro increment <=> no interface changes | |||
| # Lilv uses the same version number for both library and package | |||
| LILV_LIB_VERSION = LILV_VERSION | |||
| # Variables for 'waf dist' | |||
| APPNAME = 'lilv' | |||
| VERSION = LILV_VERSION | |||
| # Mandatory variables | |||
| top = '.' | |||
| out = 'build' | |||
| def options(opt): | |||
| opt.load('compiler_c') | |||
| opt.load('compiler_cxx') | |||
| opt.load('python') | |||
| autowaf.set_options(opt) | |||
| opt.add_option('--no-utils', action='store_true', dest='no_utils', | |||
| help='Do not build command line utilities') | |||
| opt.add_option('--bindings', action='store_true', dest='bindings', | |||
| help='Build python bindings') | |||
| opt.add_option('--dyn-manifest', action='store_true', dest='dyn_manifest', | |||
| help='Build support for dynamic manifests') | |||
| opt.add_option('--test', action='store_true', dest='build_tests', | |||
| help='Build unit tests') | |||
| opt.add_option('--no-bash-completion', action='store_true', | |||
| dest='no_bash_completion', | |||
| help='Do not install bash completion script in CONFIGDIR') | |||
| opt.add_option('--static', action='store_true', dest='static', | |||
| help='Build static library') | |||
| opt.add_option('--no-shared', action='store_true', dest='no_shared', | |||
| help='Do not build shared library') | |||
| opt.add_option('--static-progs', action='store_true', dest='static_progs', | |||
| help='Build programs as static binaries') | |||
| opt.add_option('--default-lv2-path', type='string', default='', | |||
| dest='default_lv2_path', | |||
| help='Default LV2 path to use if LV2_PATH is unset') | |||
| def configure(conf): | |||
| conf.load('compiler_c') | |||
| if Options.options.bindings: | |||
| try: | |||
| conf.load('swig') | |||
| conf.load('python') | |||
| conf.load('compiler_cxx') | |||
| conf.check_python_headers() | |||
| autowaf.define(conf, 'LILV_PYTHON', 1); | |||
| except: | |||
| pass | |||
| autowaf.configure(conf) | |||
| autowaf.set_c99_mode(conf) | |||
| autowaf.display_header('Lilv Configuration') | |||
| conf.env.BASH_COMPLETION = not Options.options.no_bash_completion | |||
| conf.env.BUILD_TESTS = Options.options.build_tests | |||
| conf.env.BUILD_UTILS = not Options.options.no_utils | |||
| conf.env.BUILD_SHARED = not Options.options.no_shared | |||
| conf.env.STATIC_PROGS = Options.options.static_progs | |||
| conf.env.BUILD_STATIC = (Options.options.static or | |||
| Options.options.static_progs) | |||
| if not conf.env.BUILD_SHARED and not conf.env.BUILD_STATIC: | |||
| conf.fatal('Neither a shared nor a static build requested') | |||
| autowaf.check_pkg(conf, 'lv2', uselib_store='LV2', | |||
| atleast_version='1.0.0', mandatory=True) | |||
| autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', | |||
| atleast_version='0.14.0', mandatory=True) | |||
| autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD', | |||
| atleast_version='0.8.0', mandatory=True) | |||
| autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM', | |||
| atleast_version='0.2.0', mandatory=True) | |||
| autowaf.define(conf, 'LILV_NEW_LV2', 1) # New LV2 discovery API | |||
| defines = ['_POSIX_C_SOURCE', '_BSD_SOURCE'] | |||
| if Options.platform == 'darwin': | |||
| defines += ['_DARWIN_C_SOURCE'] | |||
| # Check for gcov library (for test coverage) | |||
| if conf.env.BUILD_TESTS: | |||
| conf.check_cc(lib='gcov', | |||
| define_name='HAVE_GCOV', | |||
| mandatory=False) | |||
| conf.check_cc(function_name='flock', | |||
| header_name='sys/file.h', | |||
| defines=defines, | |||
| define_name='HAVE_FLOCK', | |||
| mandatory=False) | |||
| conf.check_cc(function_name='fileno', | |||
| header_name='stdio.h', | |||
| defines=defines, | |||
| define_name='HAVE_FILENO', | |||
| mandatory=False) | |||
| conf.check_cc(function_name='clock_gettime', | |||
| header_name=['sys/time.h','time.h'], | |||
| defines=['_POSIX_C_SOURCE=199309L'], | |||
| define_name='HAVE_CLOCK_GETTIME', | |||
| uselib_store='CLOCK_GETTIME', | |||
| lib=['rt'], | |||
| mandatory=False) | |||
| autowaf.define(conf, 'LILV_VERSION', LILV_VERSION) | |||
| if Options.options.dyn_manifest: | |||
| autowaf.define(conf, 'LILV_DYN_MANIFEST', 1) | |||
| lilv_path_sep = ':' | |||
| lilv_dir_sep = '/' | |||
| if sys.platform == 'win32': | |||
| lilv_path_sep = ';' | |||
| lilv_dir_sep = '\\\\' | |||
| autowaf.define(conf, 'LILV_PATH_SEP', lilv_path_sep) | |||
| autowaf.define(conf, 'LILV_DIR_SEP', lilv_dir_sep) | |||
| # Set default LV2 path | |||
| lv2_path = Options.options.default_lv2_path | |||
| if lv2_path == '': | |||
| if Options.platform == 'darwin': | |||
| lv2_path = lilv_path_sep.join(['~/Library/Audio/Plug-Ins/LV2', | |||
| '~/.lv2', | |||
| '/usr/local/lib/lv2', | |||
| '/usr/lib/lv2', | |||
| '/Library/Audio/Plug-Ins/LV2']) | |||
| elif Options.platform == 'haiku': | |||
| lv2_path = lilv_path_sep.join(['~/.lv2', | |||
| '/boot/common/add-ons/lv2']) | |||
| elif Options.platform == 'win32': | |||
| lv2_path = lilv_path_sep.join(['%APPDATA%\\\\LV2', | |||
| '%COMMONPROGRAMFILES%\\\\LV2']) | |||
| else: | |||
| libdirname = os.path.basename(conf.env.LIBDIR) | |||
| lv2_path = lilv_path_sep.join(['~/.lv2', | |||
| '/usr/%s/lv2' % libdirname, | |||
| '/usr/local/%s/lv2' % libdirname]) | |||
| autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', lv2_path) | |||
| conf.env.LIB_LILV = ['lilv-%s' % LILV_MAJOR_VERSION] | |||
| conf.write_config_header('lilv_config.h', remove=False) | |||
| autowaf.display_msg(conf, 'Default LV2_PATH', | |||
| conf.env.LILV_DEFAULT_LV2_PATH) | |||
| autowaf.display_msg(conf, 'Utilities', | |||
| bool(conf.env.BUILD_UTILS)) | |||
| autowaf.display_msg(conf, 'Unit tests', | |||
| bool(conf.env.BUILD_TESTS)) | |||
| autowaf.display_msg(conf, 'Dynamic manifest support', | |||
| bool(conf.env.LILV_DYN_MANIFEST)) | |||
| autowaf.display_msg(conf, 'Python bindings', | |||
| conf.is_defined('LILV_PYTHON')) | |||
| conf.undefine('LILV_DEFAULT_LV2_PATH') # Cmd line errors with VC++ | |||
| print('') | |||
| def build_util(bld, name, defines): | |||
| obj = bld(features = 'c cprogram', | |||
| source = name + '.c', | |||
| includes = ['.', './src', './utils'], | |||
| use = 'liblilv', | |||
| target = name, | |||
| defines = defines, | |||
| install_path = '${BINDIR}') | |||
| if not bld.env.BUILD_SHARED or bld.env.STATIC_PROGS: | |||
| obj.use = 'liblilv_static' | |||
| if bld.env.STATIC_PROGS: | |||
| if not bld.env.MSVC_COMPILER: | |||
| obj.lib = ['m'] | |||
| obj.env.SHLIB_MARKER = obj.env.STLIB_MARKER | |||
| obj.linkflags = ['-static', '-Wl,--start-group'] | |||
| return obj | |||
| def build(bld): | |||
| # C/C++ Headers | |||
| includedir = '${INCLUDEDIR}/lilv-%s/lilv' % LILV_MAJOR_VERSION | |||
| bld.install_files(includedir, bld.path.ant_glob('lilv/*.h')) | |||
| bld.install_files(includedir, bld.path.ant_glob('lilv/*.hpp')) | |||
| # Pkgconfig file | |||
| autowaf.build_pc(bld, 'LILV', LILV_VERSION, LILV_MAJOR_VERSION, [], | |||
| {'LILV_MAJOR_VERSION' : LILV_MAJOR_VERSION, | |||
| 'LILV_PKG_DEPS' : 'lv2 serd-0 sord-0 sratom-0'}) | |||
| lib_source = ''' | |||
| src/collections.c | |||
| src/instance.c | |||
| src/lib.c | |||
| src/node.c | |||
| src/plugin.c | |||
| src/pluginclass.c | |||
| src/port.c | |||
| src/query.c | |||
| src/scalepoint.c | |||
| src/state.c | |||
| src/ui.c | |||
| src/util.c | |||
| src/world.c | |||
| src/zix/tree.c | |||
| '''.split() | |||
| lib = ['dl'] | |||
| libflags = ['-fvisibility=hidden'] | |||
| defines = [] | |||
| if sys.platform == 'win32': | |||
| lib = [] | |||
| if bld.env.MSVC_COMPILER: | |||
| libflags = [] | |||
| defines = ['snprintf=_snprintf'] | |||
| elif sys.platform.find('bsd') > 0: | |||
| lib = [] | |||
| # Shared Library | |||
| if bld.env.BUILD_SHARED: | |||
| obj = bld(features = 'c cshlib', | |||
| export_includes = ['.'], | |||
| source = lib_source, | |||
| includes = ['.', './src'], | |||
| name = 'liblilv', | |||
| target = 'lilv-%s' % LILV_MAJOR_VERSION, | |||
| vnum = LILV_LIB_VERSION, | |||
| install_path = '${LIBDIR}', | |||
| defines = ['LILV_SHARED', 'LILV_INTERNAL'], | |||
| cflags = libflags, | |||
| lib = lib) | |||
| autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
| # Static library | |||
| if bld.env.BUILD_STATIC: | |||
| obj = bld(features = 'c cstlib', | |||
| export_includes = ['.'], | |||
| source = lib_source, | |||
| includes = ['.', './src'], | |||
| name = 'liblilv_static', | |||
| target = 'lilv-%s' % LILV_MAJOR_VERSION, | |||
| vnum = LILV_LIB_VERSION, | |||
| install_path = '${LIBDIR}', | |||
| defines = defines + ['LILV_INTERNAL']) | |||
| autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
| if bld.env.BUILD_TESTS: | |||
| test_libs = lib | |||
| test_cflags = [''] | |||
| if bld.is_defined('HAVE_GCOV'): | |||
| test_libs += ['gcov'] | |||
| test_cflags += ['-fprofile-arcs', '-ftest-coverage'] | |||
| # Test plugin library | |||
| penv = bld.env.derive() | |||
| shlib_pattern = penv.cshlib_PATTERN | |||
| if shlib_pattern.startswith('lib'): | |||
| shlib_pattern = shlib_pattern[3:] | |||
| penv.cshlib_PATTERN = shlib_pattern | |||
| shlib_ext = shlib_pattern[shlib_pattern.rfind('.'):] | |||
| obj = bld(features = 'c cshlib', | |||
| env = penv, | |||
| source = 'test/test_plugin.c', | |||
| name = 'test_plugin', | |||
| target = 'test/test_plugin.lv2/test_plugin', | |||
| install_path = None, | |||
| defines = defines, | |||
| cflags = test_cflags, | |||
| lib = test_libs, | |||
| uselib = 'LV2') | |||
| # Test plugin data files | |||
| for i in [ 'manifest.ttl.in', 'test_plugin.ttl.in' ]: | |||
| bld(features = 'subst', | |||
| source = 'test/' + i, | |||
| target = 'test/test_plugin.lv2/' + i.replace('.in', ''), | |||
| install_path = None, | |||
| SHLIB_EXT = shlib_ext) | |||
| # Static profiled library (for unit test code coverage) | |||
| obj = bld(features = 'c cstlib', | |||
| source = lib_source, | |||
| includes = ['.', './src'], | |||
| name = 'liblilv_profiled', | |||
| target = 'lilv_profiled', | |||
| install_path = None, | |||
| defines = defines + ['LILV_INTERNAL'], | |||
| cflags = test_cflags, | |||
| lib = test_libs) | |||
| autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
| # Unit test program | |||
| bpath = os.path.abspath(os.path.join(out, 'test', 'test_plugin.lv2')) | |||
| bpath = bpath.replace('\\', '/') | |||
| obj = bld(features = 'c cprogram', | |||
| source = 'test/lilv_test.c', | |||
| includes = ['.', './src'], | |||
| use = 'liblilv_profiled', | |||
| uselib = 'SORD LV2', | |||
| lib = test_libs, | |||
| target = 'test/lilv_test', | |||
| install_path = None, | |||
| defines = defines + ['LILV_TEST_BUNDLE=\"%s/\"' % bpath], | |||
| cflags = test_cflags) | |||
| autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') | |||
| # Utilities | |||
| if bld.env.BUILD_UTILS: | |||
| utils = ''' | |||
| utils/lilv-bench | |||
| utils/lv2info | |||
| utils/lv2ls | |||
| ''' | |||
| for i in utils.split(): | |||
| build_util(bld, i, defines) | |||
| # lv2bench (less portable than other utilities) | |||
| if bld.is_defined('HAVE_CLOCK_GETTIME'): | |||
| obj = build_util(bld, 'utils/lv2bench', defines) | |||
| if not bld.env.MSVC_COMPILER: | |||
| obj.lib = ['rt'] | |||
| # Documentation | |||
| autowaf.build_dox(bld, 'LILV', LILV_VERSION, top, out) | |||
| # Man pages | |||
| bld.install_files('${MANDIR}/man1', bld.path.ant_glob('doc/*.1')) | |||
| # Bash completion | |||
| if bld.env.BASH_COMPLETION: | |||
| bld.install_as( | |||
| '${SYSCONFDIR}/bash_completion.d/lilv', 'utils/lilv.bash_completion') | |||
| if bld.is_defined('LILV_PYTHON'): | |||
| # Python Wrapper | |||
| obj = bld(features = 'cxx cxxshlib pyext', | |||
| source = 'bindings/lilv.i', | |||
| target = 'bindings/_lilv', | |||
| includes = ['..'], | |||
| swig_flags = '-c++ -python -Wall -I.. -llilv -features autodoc=1', | |||
| use = 'liblilv') | |||
| autowaf.use_lib(bld, obj, 'LILV') | |||
| bld.install_files('${PYTHONDIR}', 'bindings/lilv.py') | |||
| bld.add_post_fun(autowaf.run_ldconfig) | |||
| if bld.env.DOCS: | |||
| bld.add_post_fun(fix_docs) | |||
| def fix_docs(ctx): | |||
| if ctx.cmd == 'build': | |||
| autowaf.make_simple_dox(APPNAME) | |||
| def upload_docs(ctx): | |||
| os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/lilv/') | |||
| def test(ctx): | |||
| autowaf.pre_test(ctx, APPNAME) | |||
| os.environ['PATH'] = 'test' + os.pathsep + os.getenv('PATH') | |||
| autowaf.run_tests(ctx, APPNAME, ['lilv_test'], dirs=['./src','./test']) | |||
| autowaf.post_test(ctx, APPNAME) | |||
| def lint(ctx): | |||
| subprocess.call('cpplint.py --filter=+whitespace/comments,-whitespace/tab,-whitespace/braces,-whitespace/labels,-build/header_guard,-readability/casting,-readability/todo,-build/include,-runtime/sizeof src/* lilv/*', shell=True) | |||
| @@ -0,0 +1,31 @@ | |||
| /* | |||
| * Carla static lilv code | |||
| * Copyright (C) 2012 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or modify | |||
| * it under the terms of the GNU General Public License as published by | |||
| * the Free Software Foundation; either version 2 of the License, or | |||
| * any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the COPYING file | |||
| */ | |||
| #include "src/collections.c" | |||
| #include "src/instance.c" | |||
| #include "src/lib.c" | |||
| #include "src/node.c" | |||
| #include "src/plugin.c" | |||
| #include "src/pluginclass.c" | |||
| #include "src/port.c" | |||
| #include "src/query.c" | |||
| #include "src/scalepoint.c" | |||
| #include "src/state.c" | |||
| #include "src/ui.c" | |||
| #include "src/util.c" | |||
| #include "src/world.c" | |||
| #include "src/zix/tree.c" | |||
| @@ -0,0 +1 @@ | |||
| David Robillard <d@drobilla.net> | |||
| @@ -0,0 +1,13 @@ | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| @@ -0,0 +1,59 @@ | |||
| Installation Instructions | |||
| ========================= | |||
| Basic Installation | |||
| ------------------ | |||
| Building this software requires only Python. To install with default options: | |||
| ./waf configure | |||
| ./waf | |||
| ./waf install | |||
| You may need to become root for the install stage, for example: | |||
| sudo ./waf install | |||
| Configuration Options | |||
| --------------------- | |||
| All supported options can be viewed using the command: | |||
| ./waf --help | |||
| Most options only need to be passed during the configure stage, for example: | |||
| ./waf configure --prefix=/usr | |||
| ./waf | |||
| ./waf install | |||
| Compiler Configuration | |||
| ---------------------- | |||
| Several standard environment variables can be used to control how compilers are | |||
| invoked: | |||
| * CC: Path to C compiler | |||
| * CFLAGS: C compiler options | |||
| * CXX: Path to C++ compiler | |||
| * CXXFLAGS: C++ compiler options | |||
| * CPPFLAGS: C preprocessor options | |||
| * LINKFLAGS: Linker options | |||
| Installation Directories | |||
| ------------------------ | |||
| The --prefix option (or the PREFIX environment variable) can be used to change | |||
| the prefix which all files are installed under. There are also several options | |||
| allowing for more fine-tuned control, see the --help output for details. | |||
| Packaging | |||
| --------- | |||
| Everything can be installed to a specific root directory by passing a --destdir | |||
| option to the install stage (or setting the DESTDIR environment variable), | |||
| which adds a prefix to all install paths. For example: | |||
| ./waf configure --prefix=/usr | |||
| ./waf | |||
| ./waf install --destdir=/tmp/package | |||
| @@ -0,0 +1,98 @@ | |||
| serd (0.18.2) stable; | |||
| * Fix crash when serd_node_new_decimal is called with infinity or NaN | |||
| * Fix crash when resolving against non-standard base URIs | |||
| * Fix bug that caused "a" abbreviation in non-predicate position | |||
| * Disable timestamps in HTML documentation for reproducible build | |||
| * Fix clashing symbol "error" in amalgamation build | |||
| * Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory) | |||
| -- David Robillard <d@drobilla.net> Sat, 22 Dec 2012 21:32:15 -0500 | |||
| serd (0.18.0) stable; | |||
| * Support digits at start of local names as per new Turtle grammar | |||
| * Add incremental read interface suitable for reading from infinite streams | |||
| * Add -e option to serdi to use incremental reading | |||
| * Add error callback to reader and writer for custom error reporting | |||
| * Add -q option to serdi to suppress all non-data output, e.g. errors | |||
| * Reset indent when finishing a write | |||
| * Report write size correctly when invalid UTF-8 is encountered and a | |||
| replacement character is written | |||
| * Strip down API documentation to a single clean page | |||
| * Fix various hyper-strict warnings | |||
| * Do not require a C++ compiler to build | |||
| * Add option to build utilities as static binaries | |||
| * Upgrade to waf 1.7.2 | |||
| -- David Robillard <d@drobilla.net> Thu, 23 Aug 2012 00:18:34 -0400 | |||
| serd (0.14.0) stable; | |||
| * Use path variables in pkgconfig files | |||
| * Install man page to DATADIR (e.g. PREFIX/share/man, not PREFIX/man) | |||
| * Tolerate invalid characters in string literals by replacing with the | |||
| Unicode replacement character | |||
| * Report reason for failure to open file in serdi | |||
| * Improve write performance by doing bulk writes for unescaped substrings | |||
| * Add SerdBulkSink for writing bulk output and corresponding serdi -B option | |||
| * Add serdi -f option to prevent URI qualification | |||
| * Remove use of multi-byte peek (readahead) and use exactly 1 page for | |||
| read buffer (instead of 2) | |||
| * Handle a quote as the last character of a long string literal in the | |||
| writer (by escaping it) rather than the reader, to avoid writing Turtle | |||
| other tools fail to parse. | |||
| * Add serd_strtod(), serd_node_new_decimal(), and serd_node_new_integer() | |||
| for locale-independent numeric node parsing/serialising. | |||
| * Add serd_file_sink for easy writing to a FILE* stream. | |||
| * Add serd_chunk_sink for easy writing to a string. | |||
| * Escape ASCII control characters in output (e.g. fix problems with string | |||
| literals that start with a backspace) | |||
| * Improve URI resolution to cover most of the abnormal cases from RFC3986 | |||
| * Support file://localhost/foo URIs in serd_uri_to_path() | |||
| * Support Windows file://c:/foo URIs in serd_uri_to_path() on all platforms | |||
| * Add serd_node_new_blob and serd_base64_decode for handling arbitrary | |||
| binary data via base64 encoding. | |||
| * Support compilation as C++ under MSVC++. | |||
| * Implement pretty-printing for collections. | |||
| * Parse collections iteratively in O(1) space. | |||
| * Report read error if both "genid" and "docid" IDs are found in the same | |||
| document, to prevent silent merging of distinct blank nodes. | |||
| * Handle files and strings that start with a UTF-8 Byte Order Mark. | |||
| * Add serd_writer_get_env(). | |||
| * Add serd_node_new_file_uri() and serd_file_uri_parse() and implement | |||
| proper URI to/from path hex escaping, etc. | |||
| * Add serd_uri_serialise_relative() for making URIs relative to a base | |||
| where possible (by chopping a common prefix and adding dot segments). | |||
| * Make URIs serialised by the writer properly escape characters. | |||
| * Add serd_writer_set_root_uri() and corresponding -r option to serdi to | |||
| enable writing URIs with up references (../). | |||
| * Resolve dot segments in serd_uri_resolve() instead of at write time. | |||
| * Add serd_reader_set_default_graph() for reading a file as a named graph. | |||
| -- David Robillard <d@drobilla.net> Tue, 17 Apr 2012 18:23:53 -0400 | |||
| serd (0.5.0) stable; | |||
| * Fix pretty printing of successive blank descriptions, i.e. "] , [" | |||
| * Avoid writing illegal Turtle names as a result of URI qualifying | |||
| * Gracefully handle NULL reader sinks | |||
| * Add serd_strerror | |||
| * Add serd_env_set_prefix_from_strings for convenience | |||
| * Fix erroneously equal SERD_ERR_BAD_SYNTAX and SERD_ERR_BAD_ARG | |||
| * Add ability to build static library | |||
| -- David Robillard <d@drobilla.net> Thu, 29 Sep 2011 00:00:00 -0400 | |||
| serd (0.4.2) stable; | |||
| * Fix compilation issues on some systems | |||
| * Fix build system Python 3 compatibility | |||
| -- David Robillard <d@drobilla.net> Wed, 25 May 2011 19:00:00 -0400 | |||
| serd (0.4.0) stable; | |||
| * Initial release | |||
| -- David Robillard <d@drobilla.net> Tue, 24 May 2011 23:00:00 -0400 | |||
| @@ -0,0 +1,29 @@ | |||
| This library is designed to allow parallel installation of different major | |||
| versions. To facilitate this, the shared library name, include directory, and | |||
| pkg-config file are suffixed with the major version number of the library. | |||
| For example, if this library was named "foo" and at version 1.x.y: | |||
| /usr/include/foo-1/foo/foo.h | |||
| /usr/lib/foo-1.so.1.x.y | |||
| /usr/lib/pkgconfig/foo-1.pc | |||
| Dependencies check for pkg-config name "foo-1" and will build | |||
| against a compatible version 1, regardless any other installed versions. | |||
| *** IMPORTANT GUIDELINES FOR PACKAGERS *** | |||
| Packages should follow the same conventions as above, i.e. include the major | |||
| version (and only the major version) in the name of the package. Continuing the | |||
| example above, the package(s) would be named foo-1 and foo-1-dev. This way, | |||
| if/when version 2 comes out, it may be installed at the same time as version 1 | |||
| without breaking anything. | |||
| Please do not create packages of this library that do not follow these | |||
| guidelines, you will break things and cause unnecessary headaches. Please do | |||
| not use any number as a suffix other than the actual major version number of the | |||
| upstream source package. | |||
| Because program and documentation names are not versioned, these should be | |||
| included in separate packages which may replace previous versions, since | |||
| there is little use in having parallel installations of them. | |||
| @@ -0,0 +1,10 @@ | |||
| Serd | |||
| ---- | |||
| Serd is a lightweight C library for RDF syntax which supports reading and | |||
| writing Turtle and NTriples. | |||
| For more information, see <http://drobilla.net/software/serd>. | |||
| -- David Robillard <d@drobilla.net> | |||
| @@ -0,0 +1,187 @@ | |||
| <doxygenlayout version="1.0"> | |||
| <!-- Navigation index tabs for HTML output --> | |||
| <navindex> | |||
| <tab type="mainpage" visible="yes" title=""/> | |||
| <tab type="pages" visible="yes" title="" intro=""/> | |||
| <tab type="modules" visible="yes" title="" intro=""/> | |||
| <tab type="namespaces" visible="yes" title=""> | |||
| <tab type="namespacelist" visible="yes" title="" intro=""/> | |||
| <tab type="namespacemembers" visible="yes" title="" intro=""/> | |||
| </tab> | |||
| <tab type="classes" visible="yes" title=""> | |||
| <tab type="classlist" visible="yes" title="" intro=""/> | |||
| <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/> | |||
| <tab type="hierarchy" visible="yes" title="" intro=""/> | |||
| <tab type="classmembers" visible="yes" title="" intro=""/> | |||
| </tab> | |||
| <tab type="files" visible="yes" title=""> | |||
| <tab type="filelist" visible="yes" title="" intro=""/> | |||
| <tab type="globals" visible="yes" title="" intro=""/> | |||
| </tab> | |||
| <tab type="examples" visible="yes" title="" intro=""/> | |||
| </navindex> | |||
| <!-- Layout definition for a class page --> | |||
| <class> | |||
| <briefdescription visible="yes"/> | |||
| <includes visible="$SHOW_INCLUDE_FILES"/> | |||
| <inheritancegraph visible="$CLASS_GRAPH"/> | |||
| <collaborationgraph visible="$COLLABORATION_GRAPH"/> | |||
| <allmemberslink visible="yes"/> | |||
| <memberdecl> | |||
| <nestedclasses visible="yes" title=""/> | |||
| <publictypes title=""/> | |||
| <publicslots title=""/> | |||
| <signals title=""/> | |||
| <publicmethods title=""/> | |||
| <publicstaticmethods title=""/> | |||
| <publicattributes title=""/> | |||
| <publicstaticattributes title=""/> | |||
| <protectedtypes title=""/> | |||
| <protectedslots title=""/> | |||
| <protectedmethods title=""/> | |||
| <protectedstaticmethods title=""/> | |||
| <protectedattributes title=""/> | |||
| <protectedstaticattributes title=""/> | |||
| <packagetypes title=""/> | |||
| <packagemethods title=""/> | |||
| <packagestaticmethods title=""/> | |||
| <packageattributes title=""/> | |||
| <packagestaticattributes title=""/> | |||
| <properties title=""/> | |||
| <events title=""/> | |||
| <privatetypes title=""/> | |||
| <privateslots title=""/> | |||
| <privatemethods title=""/> | |||
| <privatestaticmethods title=""/> | |||
| <privateattributes title=""/> | |||
| <privatestaticattributes title=""/> | |||
| <friends title=""/> | |||
| <related title="" subtitle=""/> | |||
| <membergroups visible="yes"/> | |||
| </memberdecl> | |||
| <detaileddescription title=""/> | |||
| <memberdef> | |||
| <inlineclasses title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <constructors title=""/> | |||
| <functions title=""/> | |||
| <related title=""/> | |||
| <variables title=""/> | |||
| <properties title=""/> | |||
| <events title=""/> | |||
| </memberdef> | |||
| <usedfiles visible="$SHOW_USED_FILES"/> | |||
| <authorsection visible="yes"/> | |||
| </class> | |||
| <!-- Layout definition for a namespace page --> | |||
| <namespace> | |||
| <briefdescription visible="yes"/> | |||
| <memberdecl> | |||
| <nestednamespaces visible="yes" title=""/> | |||
| <classes visible="yes" title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <functions title=""/> | |||
| <variables title=""/> | |||
| <membergroups visible="yes"/> | |||
| </memberdecl> | |||
| <detaileddescription title=""/> | |||
| <memberdef> | |||
| <inlineclasses title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <functions title=""/> | |||
| <variables title=""/> | |||
| </memberdef> | |||
| <authorsection visible="yes"/> | |||
| </namespace> | |||
| <!-- Layout definition for a file page --> | |||
| <file> | |||
| <briefdescription visible="yes"/> | |||
| <includes visible="$SHOW_INCLUDE_FILES"/> | |||
| <includegraph visible="$INCLUDE_GRAPH"/> | |||
| <includedbygraph visible="$INCLUDED_BY_GRAPH"/> | |||
| <sourcelink visible="yes"/> | |||
| <memberdecl> | |||
| <classes visible="yes" title=""/> | |||
| <namespaces visible="yes" title=""/> | |||
| <defines title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <functions title=""/> | |||
| <variables title=""/> | |||
| <membergroups visible="yes"/> | |||
| </memberdecl> | |||
| <detaileddescription title=""/> | |||
| <memberdef> | |||
| <inlineclasses title=""/> | |||
| <defines title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <functions title=""/> | |||
| <variables title=""/> | |||
| </memberdef> | |||
| <authorsection/> | |||
| </file> | |||
| <!-- Layout definition for a group page --> | |||
| <group> | |||
| <briefdescription visible="no"/> | |||
| <groupgraph visible="$GROUP_GRAPHS"/> | |||
| <detaileddescription title=""/> | |||
| <memberdecl> | |||
| <nestedgroups visible="yes" title=""/> | |||
| <dirs visible="yes" title=""/> | |||
| <files visible="yes" title=""/> | |||
| <namespaces visible="yes" title=""/> | |||
| <classes visible="yes" title=""/> | |||
| <defines title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <enumvalues title=""/> | |||
| <functions title=""/> | |||
| <variables title=""/> | |||
| <signals title=""/> | |||
| <publicslots title=""/> | |||
| <protectedslots title=""/> | |||
| <privateslots title=""/> | |||
| <events title=""/> | |||
| <properties title=""/> | |||
| <friends title=""/> | |||
| <membergroups visible="yes"/> | |||
| </memberdecl> | |||
| <memberdef> | |||
| <pagedocs/> | |||
| <inlineclasses title=""/> | |||
| <defines title=""/> | |||
| <typedefs title=""/> | |||
| <enums title=""/> | |||
| <enumvalues title=""/> | |||
| <functions title=""/> | |||
| <variables title=""/> | |||
| <signals title=""/> | |||
| <publicslots title=""/> | |||
| <protectedslots title=""/> | |||
| <privateslots title=""/> | |||
| <events title=""/> | |||
| <properties title=""/> | |||
| <friends title=""/> | |||
| </memberdef> | |||
| <authorsection visible="yes"/> | |||
| </group> | |||
| <!-- Layout definition for a directory page --> | |||
| <directory> | |||
| <briefdescription visible="yes"/> | |||
| <directorygraph visible="yes"/> | |||
| <memberdecl> | |||
| <dirs visible="yes"/> | |||
| <files visible="yes"/> | |||
| </memberdecl> | |||
| <detaileddescription title=""/> | |||
| </directory> | |||
| </doxygenlayout> | |||
| @@ -0,0 +1,72 @@ | |||
| .TH SERDI 1 "08 May 2012" | |||
| .SH NAME | |||
| .B serdi \- Read and write RDF syntax | |||
| .SH SYNOPSIS | |||
| serdi [OPTION]... INPUT BASE_URI | |||
| .SH OPTIONS | |||
| .TP | |||
| \fB\-b\fR | |||
| Fast bulk output for large serialisations. | |||
| .TP | |||
| \fB\-c PREFIX\fR | |||
| Chop PREFIX from matching blank node IDs. | |||
| .TP | |||
| \fB\-e\fR | |||
| Eat input one character at a time, rather than a page at a time which is the | |||
| default. This is useful when reading from a pipe since output will be | |||
| generated immediately as input arrives, rather than waiting until an entire | |||
| page of input has arrived. With this option serdi uses one page less memory, | |||
| but will likely be significantly slower. | |||
| .TP | |||
| \fB\-f\fR | |||
| Keep full URIs in input (don't qualify). | |||
| .TP | |||
| \fB\-h\fR | |||
| Print the command line options. | |||
| .TP | |||
| \fB\-i SYNTAX\fR | |||
| Read input in SYNTAX (`turtle' or `ntriples'). | |||
| .TP | |||
| \fB\-o SYNTAX\fR | |||
| Write output in SYNTAX (`turtle' or `ntriples'). | |||
| .TP | |||
| \fB\-p PREFIX\fR | |||
| Add PREFIX to blank node IDs. | |||
| .TP | |||
| \fB\-r ROOT_URI\fR | |||
| Keep relative URIs within ROOT_URI. | |||
| .TP | |||
| \fB\-s INPUT\fR | |||
| Parse INPUT as a string (terminates options). | |||
| .TP | |||
| \fB\-v\fR | |||
| Display version information and exit. | |||
| .SH AUTHOR | |||
| Serdi was written by David Robillard <d@drobilla.net> | |||
| .SH COPYRIGHT | |||
| Copyright \(co 2011-2012 David Robillard. | |||
| .br | |||
| License: <http://www.opensource.org/licenses/isc> | |||
| .br | |||
| This is free software; you are free to change and redistribute it. | |||
| .br | |||
| There is NO WARRANTY, to the extent permitted by law. | |||
| .SH "SEE ALSO" | |||
| <http://drobilla.net/software/serd> | |||
| @@ -0,0 +1,563 @@ | |||
| body { | |||
| font-size: medium; | |||
| font-family: sans-serif; | |||
| } | |||
| #top { | |||
| background-color: #F3F3F3; | |||
| margin: 0; | |||
| padding: 0; | |||
| border-bottom: 1px solid #DDD; | |||
| margin-bottom: 1ex; | |||
| font-size: xx-large; | |||
| font-weight: bold; | |||
| } | |||
| div.header { | |||
| display: none; | |||
| } | |||
| .tabs { | |||
| display: none; | |||
| } | |||
| h1 h2 h3 h4 h5 h6 { | |||
| font-weight: bold; | |||
| } | |||
| h1 { | |||
| font-size: 164%; | |||
| } | |||
| h2 { | |||
| font-size: 132%; | |||
| } | |||
| h3 { | |||
| font-size: 124%; | |||
| } | |||
| h4 { | |||
| font-size: 116%; | |||
| } | |||
| h5 { | |||
| font-size: 108%; | |||
| } | |||
| h6 { | |||
| font-size: 100%; | |||
| } | |||
| p { | |||
| margin: 0 0 1ex 0; | |||
| } | |||
| br { | |||
| display: none; | |||
| } | |||
| dt { | |||
| font-weight: 700; | |||
| } | |||
| div.multicol { | |||
| } | |||
| p.startli,p.startdd,p.starttd { | |||
| margin-top: 2px; | |||
| } | |||
| p.endli { | |||
| margin-bottom: 0; | |||
| } | |||
| p.enddd { | |||
| margin-bottom: 4px; | |||
| } | |||
| p.endtd { | |||
| margin-bottom: 2px; | |||
| } | |||
| caption { | |||
| font-weight: 700; | |||
| } | |||
| span.legend { | |||
| font-size: 70%; | |||
| text-align: center; | |||
| } | |||
| h3.version { | |||
| font-size: 90%; | |||
| text-align: center; | |||
| } | |||
| div.qindex,div.navtab { | |||
| background-color: #EBEFF6; | |||
| border: 1px solid #A3B4D7; | |||
| text-align: center; | |||
| margin: 2px; | |||
| padding: 2px; | |||
| } | |||
| div.qindex,div.navpath { | |||
| width: 100%; | |||
| line-height: 140%; | |||
| } | |||
| div.navtab { | |||
| margin-right: 15px; | |||
| } | |||
| /* @group Link Styling */ | |||
| a { | |||
| color: #3D8C57; | |||
| text-decoration: none; | |||
| } | |||
| .contents a:visited { | |||
| color: #50755E; | |||
| } | |||
| a:hover { | |||
| text-decoration: underline; | |||
| } | |||
| a.qindexHL { | |||
| background-color: #9CAFD4; | |||
| color: #FFF; | |||
| border: 1px double #869DCA; | |||
| } | |||
| a.code { | |||
| color: #4665A2; | |||
| } | |||
| a.codeRef { | |||
| color: #4665A2; | |||
| } | |||
| /* @end */ | |||
| dl.el { | |||
| margin-left: -1cm; | |||
| } | |||
| .fragment { | |||
| font-family: monospace, fixed; | |||
| font-size: 105%; | |||
| } | |||
| pre.fragment { | |||
| border: 1px solid #C4C4C4; | |||
| background-color: #F9F9F9; | |||
| padding: 4px 6px; | |||
| margin: 4px 8px 4px 2px; | |||
| overflow: auto; | |||
| font-size: 9pt; | |||
| line-height: 125%; | |||
| } | |||
| div.ah { | |||
| background-color: #000; | |||
| font-weight: 700; | |||
| color: #FFF; | |||
| margin-bottom: 3px; | |||
| margin-top: 3px; | |||
| padding: .2em; | |||
| border: thin solid #333; | |||
| } | |||
| div.groupHeader { | |||
| margin-left: 16px; | |||
| margin-top: 12px; | |||
| margin-bottom: 6px; | |||
| font-weight: 700; | |||
| } | |||
| div.groupText { | |||
| margin-left: 16px; | |||
| font-style: italic; | |||
| } | |||
| body { | |||
| background: #FFF; | |||
| color: #000; | |||
| margin: 0; | |||
| } | |||
| div.contents { | |||
| margin-top: 10px; | |||
| margin-left: 10px; | |||
| margin-right: 10px; | |||
| } | |||
| td.indexkey { | |||
| background-color: #EBEFF6; | |||
| font-weight: 700; | |||
| border: 1px solid #C4CFE5; | |||
| margin: 2px 0; | |||
| padding: 2px 10px; | |||
| } | |||
| td.indexvalue { | |||
| background-color: #EBEFF6; | |||
| border: 1px solid #C4CFE5; | |||
| padding: 2px 10px; | |||
| margin: 2px 0; | |||
| } | |||
| tr.memlist { | |||
| background-color: #EEF1F7; | |||
| } | |||
| p.formulaDsp { | |||
| text-align: center; | |||
| } | |||
| img.formulaDsp { | |||
| } | |||
| img.formulaInl { | |||
| vertical-align: middle; | |||
| } | |||
| div.center { | |||
| text-align: center; | |||
| margin-top: 0; | |||
| margin-bottom: 0; | |||
| padding: 0; | |||
| } | |||
| div.center img { | |||
| border: 0; | |||
| } | |||
| address.footer { | |||
| text-align: right; | |||
| padding: 0 0.25em 0.25em 0; | |||
| } | |||
| img.footer { | |||
| border: 0; | |||
| vertical-align: middle; | |||
| } | |||
| /* @group Code Colorization */ | |||
| span.keyword { | |||
| color: green; | |||
| } | |||
| span.keywordtype { | |||
| color: #604020; | |||
| } | |||
| span.keywordflow { | |||
| color: #e08000; | |||
| } | |||
| span.comment { | |||
| color: maroon; | |||
| } | |||
| span.preprocessor { | |||
| color: #806020; | |||
| } | |||
| span.stringliteral { | |||
| color: #002080; | |||
| } | |||
| span.charliteral { | |||
| color: teal; | |||
| } | |||
| span.vhdldigit { | |||
| color: #F0F; | |||
| } | |||
| span.vhdlkeyword { | |||
| color: #700070; | |||
| } | |||
| span.vhdllogic { | |||
| color: red; | |||
| } | |||
| /* @end */ | |||
| td.tiny { | |||
| font-size: 75%; | |||
| } | |||
| .dirtab { | |||
| padding: 4px; | |||
| border-collapse: collapse; | |||
| border: 1px solid #A3B4D7; | |||
| } | |||
| th.dirtab { | |||
| background: #EBEFF6; | |||
| font-weight: 700; | |||
| } | |||
| hr { | |||
| height: 0; | |||
| border: none; | |||
| border-top: 1px solid #DDD; | |||
| margin: 2em 0 1em; | |||
| } | |||
| hr.footer { | |||
| height: 1px; | |||
| } | |||
| /* @group Member Descriptions */ | |||
| table.memberdecls { | |||
| border-spacing: 0; | |||
| font-size: small; | |||
| } | |||
| .mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
| background-color: #FBFBFB; | |||
| margin: 0; | |||
| padding: 0.25ex; | |||
| } | |||
| .mdescLeft,.mdescRight { | |||
| color: #555; | |||
| } | |||
| .memItemLeft,.memItemRight,.memTemplParams { | |||
| border-top: 1px solid #DDD; | |||
| } | |||
| .memItemLeft,.memTemplItemLeft { | |||
| white-space: nowrap; | |||
| padding-left: 2em; | |||
| } | |||
| .memTemplParams { | |||
| color: #464646; | |||
| white-space: nowrap; | |||
| } | |||
| /* @end */ | |||
| /* @group Member Details */ | |||
| /* Styles for detailed member documentation */ | |||
| .memtemplate { | |||
| font-size: 80%; | |||
| color: #4665A2; | |||
| font-weight: bold; | |||
| } | |||
| .memnav { | |||
| background-color: #EBEFF6; | |||
| border: 1px solid #A3B4D7; | |||
| text-align: center; | |||
| margin: 2px; | |||
| margin-right: 15px; | |||
| padding: 2px; | |||
| } | |||
| .memitem { | |||
| padding: 0; | |||
| margin: 1ex 0 2ex 0; | |||
| border: 1px solid #CCC; | |||
| } | |||
| .memname { | |||
| white-space: nowrap; | |||
| font-weight: bold; | |||
| } | |||
| .memproto { | |||
| border-bottom: 1px solid #DDD; | |||
| padding: 0.5ex; | |||
| font-weight: bold; | |||
| background-color: #F3F3F3; | |||
| } | |||
| .memdoc { | |||
| padding: 1ex; | |||
| background-color: #FBFBFB; | |||
| border-top-width: 0; | |||
| } | |||
| .paramkey { | |||
| text-align: right; | |||
| } | |||
| .paramtype { | |||
| white-space: nowrap; | |||
| } | |||
| .paramname { | |||
| color: #602020; | |||
| white-space: nowrap; | |||
| } | |||
| .paramname em { | |||
| font-style: normal; | |||
| } | |||
| /* @end */ | |||
| /* @group Directory (tree) */ | |||
| /* for the tree view */ | |||
| .ftvtree { | |||
| font-family: sans-serif; | |||
| margin: 0; | |||
| } | |||
| /* these are for tree view when used as main index */ | |||
| .directory { | |||
| font-size: 9pt; | |||
| font-weight: bold; | |||
| margin: 5px; | |||
| } | |||
| .directory h3 { | |||
| margin: 0; | |||
| margin-top: 1em; | |||
| font-size: 11pt; | |||
| } | |||
| .directory > h3 { | |||
| margin-top: 0; | |||
| } | |||
| .directory p { | |||
| margin: 0; | |||
| white-space: nowrap; | |||
| } | |||
| .directory div { | |||
| display: none; | |||
| margin: 0; | |||
| } | |||
| .directory img { | |||
| vertical-align: -30%; | |||
| } | |||
| /* these are for tree view when not used as main index */ | |||
| .directory-alt { | |||
| font-size: 100%; | |||
| font-weight: bold; | |||
| } | |||
| .directory-alt h3 { | |||
| margin: 0; | |||
| margin-top: 1em; | |||
| font-size: 11pt; | |||
| } | |||
| .directory-alt > h3 { | |||
| margin-top: 0; | |||
| } | |||
| .directory-alt p { | |||
| margin: 0; | |||
| white-space: nowrap; | |||
| } | |||
| .directory-alt div { | |||
| display: none; | |||
| margin: 0; | |||
| } | |||
| .directory-alt img { | |||
| vertical-align: -30%; | |||
| } | |||
| /* @end */ | |||
| div.dynheader { | |||
| margin-top: 8px; | |||
| } | |||
| address { | |||
| font-style: normal; | |||
| color: #2A3D61; | |||
| } | |||
| table.doxtable { | |||
| border-collapse: collapse; | |||
| margin: 0.5ex; | |||
| } | |||
| table.doxtable td,table.doxtable th { | |||
| border: 1px solid #DDD; | |||
| padding: 3px 7px 2px; | |||
| } | |||
| table.doxtable th { | |||
| background-color: #F3F3F3; | |||
| color: #000; | |||
| padding-bottom: 4px; | |||
| padding-top: 5px; | |||
| text-align: left; | |||
| font-weight: bold; | |||
| } | |||
| .tabsearch { | |||
| top: 0; | |||
| left: 10px; | |||
| height: 36px; | |||
| z-index: 101; | |||
| overflow: hidden; | |||
| font-size: 13px; | |||
| } | |||
| .navpath ul { | |||
| font-size: 11px; | |||
| height: 30px; | |||
| line-height: 30px; | |||
| color: #8AA0CC; | |||
| border: 1px solid #C2CDE4; | |||
| overflow: hidden; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .navpath li { | |||
| list-style-type: none; | |||
| float: left; | |||
| padding-left: 10px; | |||
| padding-right: 15px; | |||
| color: #364D7C; | |||
| } | |||
| .navpath a { | |||
| height: 32px; | |||
| display: block; | |||
| text-decoration: none; | |||
| outline: none; | |||
| } | |||
| .navpath a:hover { | |||
| color: #6884BD; | |||
| } | |||
| div.summary { | |||
| float: right; | |||
| font-size: 8pt; | |||
| padding-right: 5px; | |||
| width: 50%; | |||
| text-align: right; | |||
| } | |||
| div.summary a { | |||
| white-space: nowrap; | |||
| } | |||
| div.header { | |||
| background-color: #F3F3F3; | |||
| margin: 0; | |||
| border-bottom: 1px solid #DDD; | |||
| } | |||
| div.headertitle { | |||
| padding: 5px 5px 5px 10px; | |||
| font-size: 180%; | |||
| font-weight: bold; | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| prefix=@PREFIX@ | |||
| exec_prefix=@EXEC_PREFIX@ | |||
| libdir=@LIBDIR@ | |||
| includedir=@INCLUDEDIR@ | |||
| Name: Serd | |||
| Version: @SERD_VERSION@ | |||
| Description: Lightweight RDF syntax library | |||
| Libs: -L${libdir} -l@LIB_SERD@ | |||
| Cflags: -I${includedir}/serd-@SERD_MAJOR_VERSION@ | |||
| @@ -0,0 +1,963 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| /** | |||
| @file serd.h API for Serd, a lightweight RDF syntax library. | |||
| */ | |||
| #ifndef SERD_SERD_H | |||
| #define SERD_SERD_H | |||
| #include <stdarg.h> | |||
| #include <stddef.h> | |||
| #include <stdint.h> | |||
| #include <stdio.h> | |||
| #ifdef SERD_SHARED | |||
| # ifdef _WIN32 | |||
| # define SERD_LIB_IMPORT __declspec(dllimport) | |||
| # define SERD_LIB_EXPORT __declspec(dllexport) | |||
| # else | |||
| # define SERD_LIB_IMPORT __attribute__((visibility("default"))) | |||
| # define SERD_LIB_EXPORT __attribute__((visibility("default"))) | |||
| # endif | |||
| # ifdef SERD_INTERNAL | |||
| # define SERD_API SERD_LIB_EXPORT | |||
| # else | |||
| # define SERD_API SERD_LIB_IMPORT | |||
| # endif | |||
| #else | |||
| # define SERD_API | |||
| #endif | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #else | |||
| # include <stdbool.h> | |||
| #endif | |||
| /** | |||
| @defgroup serd Serd | |||
| A lightweight RDF syntax library. | |||
| @{ | |||
| */ | |||
| /** | |||
| Environment. | |||
| Represents the state required to resolve a CURIE or relative URI, e.g. the | |||
| base URI and set of namespace prefixes at a particular point. | |||
| */ | |||
| typedef struct SerdEnvImpl SerdEnv; | |||
| /** | |||
| RDF reader. | |||
| Parses RDF by calling user-provided sink functions as input is consumed | |||
| (much like an XML SAX parser). | |||
| */ | |||
| typedef struct SerdReaderImpl SerdReader; | |||
| /** | |||
| RDF writer. | |||
| Provides a number of functions to allow writing RDF syntax out to some | |||
| stream. These functions are deliberately compatible with the sink functions | |||
| used by SerdReader, so a reader can be directly connected to a writer to | |||
| re-serialise a document with minimal overhead. | |||
| */ | |||
| typedef struct SerdWriterImpl SerdWriter; | |||
| /** | |||
| Return status code. | |||
| */ | |||
| typedef enum { | |||
| SERD_SUCCESS, /**< No error */ | |||
| SERD_FAILURE, /**< Non-fatal failure */ | |||
| SERD_ERR_UNKNOWN, /**< Unknown error */ | |||
| SERD_ERR_BAD_SYNTAX, /**< Invalid syntax */ | |||
| SERD_ERR_BAD_ARG, /**< Invalid argument */ | |||
| SERD_ERR_NOT_FOUND, /**< Not found */ | |||
| SERD_ERR_ID_CLASH, /**< Encountered clashing blank node IDs */ | |||
| SERD_ERR_BAD_CURIE, /**< Invalid CURIE (e.g. prefix does not exist) */ | |||
| SERD_ERR_INTERNAL /**< Unexpected internal error (should not happen) */ | |||
| } SerdStatus; | |||
| /** | |||
| RDF syntax type. | |||
| */ | |||
| typedef enum { | |||
| /** | |||
| Turtle - Terse RDF Triple Language (UTF-8). | |||
| @see <a href="http://www.w3.org/TeamSubmission/turtle/">Turtle</a> | |||
| */ | |||
| SERD_TURTLE = 1, | |||
| /** | |||
| NTriples - Line-based RDF triples (ASCII). | |||
| @see <a href="http://www.w3.org/TR/rdf-testcases#ntriples">NTriples</a> | |||
| */ | |||
| SERD_NTRIPLES = 2 | |||
| } SerdSyntax; | |||
| /** | |||
| Flags indication inline abbreviation information for a statement. | |||
| */ | |||
| typedef enum { | |||
| SERD_EMPTY_S = 1 << 1, /**< Empty blank node subject */ | |||
| SERD_EMPTY_O = 1 << 2, /**< Empty blank node object */ | |||
| SERD_ANON_S_BEGIN = 1 << 3, /**< Start of anonymous subject */ | |||
| SERD_ANON_O_BEGIN = 1 << 4, /**< Start of anonymous object */ | |||
| SERD_ANON_CONT = 1 << 5, /**< Continuation of anonymous node */ | |||
| SERD_LIST_S_BEGIN = 1 << 6, /**< Start of list subject */ | |||
| SERD_LIST_O_BEGIN = 1 << 7, /**< Start of list object */ | |||
| SERD_LIST_CONT = 1 << 8 /**< Continuation of list */ | |||
| } SerdStatementFlag; | |||
| /** | |||
| Bitwise OR of SerdNodeFlag values. | |||
| */ | |||
| typedef uint32_t SerdStatementFlags; | |||
| /** | |||
| Type of a syntactic RDF node. | |||
| This is more precise than the type of an abstract RDF node. An abstract | |||
| node is either a resource, literal, or blank. In syntax there are two ways | |||
| to refer to a resource (by URI or CURIE) and two ways to refer to a blank | |||
| (by ID or anonymously). Anonymous (inline) blank nodes are expressed using | |||
| SerdStatementFlags rather than this type. | |||
| */ | |||
| typedef enum { | |||
| /** | |||
| The type of a nonexistent node. | |||
| This type is useful as a sentinel, but is never emitted by the reader. | |||
| */ | |||
| SERD_NOTHING = 0, | |||
| /** | |||
| Literal value. | |||
| A literal optionally has either a language, or a datatype (not both). | |||
| */ | |||
| SERD_LITERAL = 1, | |||
| /** | |||
| URI (absolute or relative). | |||
| Value is an unquoted URI string, which is either a relative reference | |||
| with respect to the current base URI (e.g. "foo/bar"), or an absolute | |||
| URI (e.g. "http://example.org/foo"). | |||
| @see <a href="http://tools.ietf.org/html/rfc3986">RFC3986</a>. | |||
| */ | |||
| SERD_URI = 2, | |||
| /** | |||
| CURIE, a shortened URI. | |||
| Value is an unquoted CURIE string relative to the current environment, | |||
| e.g. "rdf:type". | |||
| @see <a href="http://www.w3.org/TR/curie">CURIE Syntax 1.0</a> | |||
| */ | |||
| SERD_CURIE = 3, | |||
| /** | |||
| A blank node. | |||
| Value is a blank node ID, e.g. "id3", which is meaningful only within | |||
| this serialisation. | |||
| @see <a href="http://www.w3.org/TeamSubmission/turtle#nodeID">Turtle | |||
| <tt>nodeID</tt></a> | |||
| */ | |||
| SERD_BLANK = 4, | |||
| } SerdType; | |||
| /** | |||
| Flags indicating certain string properties relevant to serialisation. | |||
| */ | |||
| typedef enum { | |||
| SERD_HAS_NEWLINE = 1, /**< Contains line breaks ('\\n' or '\\r') */ | |||
| SERD_HAS_QUOTE = 1 << 1 /**< Contains quotes ('"') */ | |||
| } SerdNodeFlag; | |||
| /** | |||
| Bitwise OR of SerdNodeFlag values. | |||
| */ | |||
| typedef uint32_t SerdNodeFlags; | |||
| /** | |||
| A syntactic RDF node. | |||
| */ | |||
| typedef struct { | |||
| const uint8_t* buf; /**< Value string */ | |||
| size_t n_bytes; /**< Size in bytes (not including null) */ | |||
| size_t n_chars; /**< Length in characters (not including null)*/ | |||
| SerdNodeFlags flags; /**< Node flags (e.g. string properties) */ | |||
| SerdType type; /**< Node type */ | |||
| } SerdNode; | |||
| /** | |||
| An unterminated string fragment. | |||
| */ | |||
| typedef struct { | |||
| const uint8_t* buf; /**< Start of chunk */ | |||
| size_t len; /**< Length of chunk in bytes */ | |||
| } SerdChunk; | |||
| /** | |||
| An error description. | |||
| */ | |||
| typedef struct { | |||
| SerdStatus status; /**< Error code */ | |||
| const uint8_t* filename; /**< File where error was encountered, or NULL */ | |||
| unsigned line; /**< Line where error was encountered, or 0 */ | |||
| unsigned col; /**< Column where error was encountered */ | |||
| const char* fmt; /**< Message format string (printf style) */ | |||
| va_list* args; /**< Arguments for fmt */ | |||
| } SerdError; | |||
| /** | |||
| A parsed URI. | |||
| This struct directly refers to chunks in other strings, it does not own any | |||
| memory itself. Thus, URIs can be parsed and/or resolved against a base URI | |||
| in-place without allocating memory. | |||
| */ | |||
| typedef struct { | |||
| SerdChunk scheme; /**< Scheme */ | |||
| SerdChunk authority; /**< Authority */ | |||
| SerdChunk path_base; /**< Path prefix if relative */ | |||
| SerdChunk path; /**< Path suffix */ | |||
| SerdChunk query; /**< Query */ | |||
| SerdChunk fragment; /**< Fragment */ | |||
| } SerdURI; | |||
| /** | |||
| Syntax style options. | |||
| The style of the writer output can be controlled by ORing together | |||
| values from this enumeration. Note that some options are only supported | |||
| for some syntaxes (e.g. NTriples does not support abbreviation and is | |||
| always ASCII). | |||
| */ | |||
| typedef enum { | |||
| SERD_STYLE_ABBREVIATED = 1, /**< Abbreviate triples when possible. */ | |||
| SERD_STYLE_ASCII = 1 << 1, /**< Escape all non-ASCII characters. */ | |||
| SERD_STYLE_RESOLVED = 1 << 2, /**< Resolve URIs against base URI. */ | |||
| SERD_STYLE_CURIED = 1 << 3, /**< Shorten URIs into CURIEs. */ | |||
| SERD_STYLE_BULK = 1 << 4 /**< Write output in pages. */ | |||
| } SerdStyle; | |||
| /** | |||
| @name String Utilities | |||
| @{ | |||
| */ | |||
| /** | |||
| Return a string describing a status code. | |||
| */ | |||
| SERD_API | |||
| const uint8_t* | |||
| serd_strerror(SerdStatus status); | |||
| /** | |||
| Measure a UTF-8 string. | |||
| @return Length of @c str in characters (except NULL). | |||
| @param str A null-terminated UTF-8 string. | |||
| @param n_bytes (Output) Set to the size of @c str in bytes (except NULL). | |||
| @param flags (Output) Set to the applicable flags. | |||
| */ | |||
| SERD_API | |||
| size_t | |||
| serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags); | |||
| /** | |||
| Parse a string to a double. | |||
| The API of this function is identical to the standard C strtod function, | |||
| except this function is locale-independent and always matches the lexical | |||
| format used in the Turtle grammar (the decimal point is always "."). | |||
| */ | |||
| SERD_API | |||
| double | |||
| serd_strtod(const char* str, char** endptr); | |||
| /** | |||
| Decode a base64 string. | |||
| This function can be used to deserialise a blob node created with | |||
| serd_node_new_blob(). | |||
| @param str Base64 string to decode. | |||
| @param len The length of @c str. | |||
| @param size Set to the size of the returned blob in bytes. | |||
| @return A newly allocated blob which must be freed with free(). | |||
| */ | |||
| SERD_API | |||
| void* | |||
| serd_base64_decode(const uint8_t* str, size_t len, size_t* size); | |||
| /** | |||
| @} | |||
| @name URI | |||
| @{ | |||
| */ | |||
| static const SerdURI SERD_URI_NULL = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; | |||
| /** | |||
| Return the local path for @c uri, or NULL if @c uri is not a file URI. | |||
| Note this (inappropriately named) function only removes the file scheme if | |||
| necessary, and returns @c uri unmodified if it is an absolute path. Percent | |||
| encoding and other issues are not handled, to properly convert a file URI to | |||
| a path, use serd_file_uri_parse(). | |||
| */ | |||
| SERD_API | |||
| const uint8_t* | |||
| serd_uri_to_path(const uint8_t* uri); | |||
| /** | |||
| Get the unescaped path and hostname from a file URI. | |||
| @param uri A file URI. | |||
| @param hostname If non-NULL, set to the hostname, if present. | |||
| @return The path component of the URI. | |||
| Both the returned path and @c hostname (if applicable) are owned by the | |||
| caller and must be freed with free(). | |||
| */ | |||
| SERD_API | |||
| uint8_t* | |||
| serd_file_uri_parse(const uint8_t* uri, uint8_t** hostname); | |||
| /** | |||
| Return true iff @c utf8 starts with a valid URI scheme. | |||
| */ | |||
| SERD_API | |||
| bool | |||
| serd_uri_string_has_scheme(const uint8_t* utf8); | |||
| /** | |||
| Parse @c utf8, writing result to @c out. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_uri_parse(const uint8_t* utf8, SerdURI* out); | |||
| /** | |||
| Set @c out to @c uri resolved against @c base. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_uri_resolve(const SerdURI* uri, const SerdURI* base, SerdURI* out); | |||
| /** | |||
| Sink function for raw string output. | |||
| */ | |||
| typedef size_t (*SerdSink)(const void* buf, size_t len, void* stream); | |||
| /** | |||
| Serialise @c uri with a series of calls to @c sink. | |||
| */ | |||
| SERD_API | |||
| size_t | |||
| serd_uri_serialise(const SerdURI* uri, SerdSink sink, void* stream); | |||
| /** | |||
| Serialise @c uri relative to @c base with a series of calls to @c sink. | |||
| The @c uri is written as a relative URI iff if it a child of @c base and @c | |||
| root. The optional @c root parameter must be a prefix of @c base and can be | |||
| used keep up-references ("../") within a certain namespace. | |||
| */ | |||
| SERD_API | |||
| size_t | |||
| serd_uri_serialise_relative(const SerdURI* uri, | |||
| const SerdURI* base, | |||
| const SerdURI* root, | |||
| SerdSink sink, | |||
| void* stream); | |||
| /** | |||
| @} | |||
| @name Node | |||
| @{ | |||
| */ | |||
| static const SerdNode SERD_NODE_NULL = { 0, 0, 0, 0, SERD_NOTHING }; | |||
| /** | |||
| Make a (shallow) node from @c str. | |||
| This measures, but does not copy, @c str. No memory is allocated. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_from_string(SerdType type, const uint8_t* str); | |||
| /** | |||
| Make a deep copy of @c node. | |||
| @return a node that the caller must free with @ref serd_node_free. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_copy(const SerdNode* node); | |||
| /** | |||
| Return true iff @c a is equal to @c b. | |||
| */ | |||
| SERD_API | |||
| bool | |||
| serd_node_equals(const SerdNode* a, const SerdNode* b); | |||
| /** | |||
| Simple wrapper for serd_node_new_uri to resolve a URI node. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_uri_from_node(const SerdNode* uri_node, | |||
| const SerdURI* base, | |||
| SerdURI* out); | |||
| /** | |||
| Simple wrapper for serd_node_new_uri to resolve a URI string. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_uri_from_string(const uint8_t* str, | |||
| const SerdURI* base, | |||
| SerdURI* out); | |||
| /** | |||
| Create a new file URI node from a file system path and optional hostname. | |||
| Backslashes in Windows paths will be converted and '%' will always be | |||
| percent encoded. If @c escape is true, all other invalid characters will be | |||
| percent encoded as well. | |||
| If @c path is relative, @c hostname is ignored. | |||
| If @c out is not NULL, it will be set to the parsed URI. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_file_uri(const uint8_t* path, | |||
| const uint8_t* hostname, | |||
| SerdURI* out, | |||
| bool escape); | |||
| /** | |||
| Create a new node by serialising @c uri into a new string. | |||
| @param uri The URI to parse and serialise. | |||
| @param base Base URI to resolve @c uri against (or NULL for no resolution). | |||
| @param out Set to the parsing of the new URI (i.e. points only to | |||
| memory owned by the new returned node). | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out); | |||
| /** | |||
| Create a new node by serialising @c d into an xsd:decimal string. | |||
| The resulting node will always contain a `.', start with a digit, and end | |||
| with a digit (i.e. will have a leading and/or trailing `0' if necessary). | |||
| It will never be in scientific notation. A maximum of @c frac_digits digits | |||
| will be written after the decimal point, but trailing zeros will | |||
| automatically be omitted (except one if @c d is a round integer). | |||
| Note that about 16 and 8 fractional digits are required to precisely | |||
| represent a double and float, respectively. | |||
| @param d The value for the new node. | |||
| @param frac_digits The maximum number of digits after the decimal place. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_decimal(double d, unsigned frac_digits); | |||
| /** | |||
| Create a new node by serialising @c i into an xsd:integer string. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_integer(int64_t i); | |||
| /** | |||
| Create a node by serialising @c buf into an xsd:base64Binary string. | |||
| This function can be used to make a serialisable node out of arbitrary | |||
| binary data, which can be decoded using serd_base64_decode(). | |||
| @param buf Raw binary input data. | |||
| @param size Size of @c buf. | |||
| @param wrap_lines Wrap lines at 76 characters to conform to RFC 2045. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_blob(const void* buf, size_t size, bool wrap_lines); | |||
| /** | |||
| Free any data owned by @c node. | |||
| Note that if @c node is itself dynamically allocated (which is not the case | |||
| for nodes created internally by serd), it will not be freed. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_node_free(SerdNode* node); | |||
| /** | |||
| @} | |||
| @name Event Handlers | |||
| @{ | |||
| */ | |||
| /** | |||
| Sink (callback) for errors. | |||
| @param handle Handle for user data. | |||
| @param error Error description. | |||
| */ | |||
| typedef SerdStatus (*SerdErrorSink)(void* handle, | |||
| const SerdError* error); | |||
| /** | |||
| Sink (callback) for base URI changes. | |||
| Called whenever the base URI of the serialisation changes. | |||
| */ | |||
| typedef SerdStatus (*SerdBaseSink)(void* handle, | |||
| const SerdNode* uri); | |||
| /** | |||
| Sink (callback) for namespace definitions. | |||
| Called whenever a prefix is defined in the serialisation. | |||
| */ | |||
| typedef SerdStatus (*SerdPrefixSink)(void* handle, | |||
| const SerdNode* name, | |||
| const SerdNode* uri); | |||
| /** | |||
| Sink (callback) for statements. | |||
| Called for every RDF statement in the serialisation. | |||
| */ | |||
| typedef SerdStatus (*SerdStatementSink)(void* handle, | |||
| SerdStatementFlags flags, | |||
| const SerdNode* graph, | |||
| const SerdNode* subject, | |||
| const SerdNode* predicate, | |||
| const SerdNode* object, | |||
| const SerdNode* object_datatype, | |||
| const SerdNode* object_lang); | |||
| /** | |||
| Sink (callback) for anonymous node end markers. | |||
| This is called to indicate that the anonymous node with the given | |||
| @c value will no longer be referred to by any future statements | |||
| (i.e. the anonymous serialisation of the node is finished). | |||
| */ | |||
| typedef SerdStatus (*SerdEndSink)(void* handle, | |||
| const SerdNode* node); | |||
| /** | |||
| @} | |||
| @name Environment | |||
| @{ | |||
| */ | |||
| /** | |||
| Create a new environment. | |||
| */ | |||
| SERD_API | |||
| SerdEnv* | |||
| serd_env_new(const SerdNode* base_uri); | |||
| /** | |||
| Free @c ns. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_env_free(SerdEnv* env); | |||
| /** | |||
| Get the current base URI. | |||
| */ | |||
| SERD_API | |||
| const SerdNode* | |||
| serd_env_get_base_uri(const SerdEnv* env, | |||
| SerdURI* out); | |||
| /** | |||
| Set the current base URI. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_set_base_uri(SerdEnv* env, | |||
| const SerdNode* uri); | |||
| /** | |||
| Set a namespace prefix. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_set_prefix(SerdEnv* env, | |||
| const SerdNode* name, | |||
| const SerdNode* uri); | |||
| /** | |||
| Set a namespace prefix. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_set_prefix_from_strings(SerdEnv* env, | |||
| const uint8_t* name, | |||
| const uint8_t* uri); | |||
| /** | |||
| Qualify @c uri into a CURIE if possible. | |||
| */ | |||
| SERD_API | |||
| bool | |||
| serd_env_qualify(const SerdEnv* env, | |||
| const SerdNode* uri, | |||
| SerdNode* prefix, | |||
| SerdChunk* suffix); | |||
| /** | |||
| Expand @c curie. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_expand(const SerdEnv* env, | |||
| const SerdNode* curie, | |||
| SerdChunk* uri_prefix, | |||
| SerdChunk* uri_suffix); | |||
| /** | |||
| Expand @c node, which must be a CURIE or URI, to a full URI. | |||
| */ | |||
| SERD_API | |||
| SerdNode | |||
| serd_env_expand_node(const SerdEnv* env, | |||
| const SerdNode* node); | |||
| /** | |||
| Call @c func for each prefix defined in @c env. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_env_foreach(const SerdEnv* env, | |||
| SerdPrefixSink func, | |||
| void* handle); | |||
| /** | |||
| @} | |||
| @name Reader | |||
| @{ | |||
| */ | |||
| /** | |||
| Create a new RDF reader. | |||
| */ | |||
| SERD_API | |||
| SerdReader* | |||
| serd_reader_new(SerdSyntax syntax, | |||
| void* handle, | |||
| void (*free_handle)(void*), | |||
| SerdBaseSink base_sink, | |||
| SerdPrefixSink prefix_sink, | |||
| SerdStatementSink statement_sink, | |||
| SerdEndSink end_sink); | |||
| /** | |||
| Set a function to be called when errors occur during reading. | |||
| The @p error_sink will be called with @p handle as its first argument. If | |||
| no error function is set, errors are printed to stderr in GCC style. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_reader_set_error_sink(SerdReader* reader, | |||
| SerdErrorSink error_sink, | |||
| void* handle); | |||
| /** | |||
| Return the @c handle passed to @ref serd_reader_new. | |||
| */ | |||
| SERD_API | |||
| void* | |||
| serd_reader_get_handle(const SerdReader* reader); | |||
| /** | |||
| Set a prefix to be added to all blank node identifiers. | |||
| This is useful when multiple files are to be parsed into the same output | |||
| (e.g. a store, or other files). Since Serd preserves blank node IDs, this | |||
| could cause conflicts where two non-equivalent blank nodes are merged, | |||
| resulting in corrupt data. By setting a unique blank node prefix for each | |||
| parsed file, this can be avoided, while preserving blank node names. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_reader_add_blank_prefix(SerdReader* reader, | |||
| const uint8_t* prefix); | |||
| /** | |||
| Set the URI of the default graph. | |||
| If this is set, the reader will emit quads with the graph set to the given | |||
| node for any statements that are not in a named graph (which is currently | |||
| all of them since Serd currently does not support any graph syntaxes). | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_reader_set_default_graph(SerdReader* reader, | |||
| const SerdNode* graph); | |||
| /** | |||
| Read a file at a given @c uri. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_reader_read_file(SerdReader* reader, | |||
| const uint8_t* uri); | |||
| /** | |||
| Start an incremental read from a file handle. | |||
| Iff @p bulk is true, @p file will be read a page at a time. This is more | |||
| efficient, but uses a page of memory and means that an entire page of input | |||
| must be ready before any callbacks will fire. To react as soon as input | |||
| arrives, set @p bulk to false. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_reader_start_stream(SerdReader* me, | |||
| FILE* file, | |||
| const uint8_t* name, | |||
| bool bulk); | |||
| /** | |||
| Read a single "chunk" of data during an incremental read. | |||
| This function will read a single top level description, and return. This | |||
| may be a directive, statement, or several statements; essentially it reads | |||
| until a '.' is encountered. This is particularly useful for reading | |||
| directly from a pipe or socket. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_reader_read_chunk(SerdReader* me); | |||
| /** | |||
| Finish an incremental read from a file handle. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_reader_end_stream(SerdReader* me); | |||
| /** | |||
| Read @c file. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_reader_read_file_handle(SerdReader* reader, | |||
| FILE* file, | |||
| const uint8_t* name); | |||
| /** | |||
| Read @c utf8. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_reader_read_string(SerdReader* me, const uint8_t* utf8); | |||
| /** | |||
| Free @c reader. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_reader_free(SerdReader* reader); | |||
| /** | |||
| @} | |||
| @name Writer | |||
| @{ | |||
| */ | |||
| /** | |||
| Create a new RDF writer. | |||
| */ | |||
| SERD_API | |||
| SerdWriter* | |||
| serd_writer_new(SerdSyntax syntax, | |||
| SerdStyle style, | |||
| SerdEnv* env, | |||
| const SerdURI* base_uri, | |||
| SerdSink sink, | |||
| void* stream); | |||
| /** | |||
| Free @c writer. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_writer_free(SerdWriter* writer); | |||
| /** | |||
| Return the env used by @c writer. | |||
| */ | |||
| SERD_API | |||
| SerdEnv* | |||
| serd_writer_get_env(SerdWriter* writer); | |||
| /** | |||
| A convenience sink function for writing to a FILE*. | |||
| This function can be used as a SerdSink when writing to a FILE*. The | |||
| @c stream parameter must be a FILE* opened for writing. | |||
| */ | |||
| SERD_API | |||
| size_t | |||
| serd_file_sink(const void* buf, size_t len, void* stream); | |||
| /** | |||
| A convenience sink function for writing to a string. | |||
| This function can be used as a SerdSink to write to a SerdChunk which is | |||
| resized as necessary with realloc(). The @c stream parameter must point to | |||
| an initialized SerdChunk. When the write is finished, the string should be | |||
| retrieved with serd_chunk_sink_finish(). | |||
| */ | |||
| SERD_API | |||
| size_t | |||
| serd_chunk_sink(const void* buf, size_t len, void* stream); | |||
| /** | |||
| Finish a serialisation to a chunk with serd_chunk_sink(). | |||
| The returned string is the result of the serialisation, which is NULL | |||
| terminated (by this function) and owned by the caller. | |||
| */ | |||
| SERD_API | |||
| uint8_t* | |||
| serd_chunk_sink_finish(SerdChunk* stream); | |||
| /** | |||
| Set a function to be called when errors occur during writing. | |||
| The @p error_sink will be called with @p handle as its first argument. If | |||
| no error function is set, errors are printed to stderr. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_writer_set_error_sink(SerdWriter* writer, | |||
| SerdErrorSink error_sink, | |||
| void* handle); | |||
| /** | |||
| Set a prefix to be removed from matching blank node identifiers. | |||
| */ | |||
| SERD_API | |||
| void | |||
| serd_writer_chop_blank_prefix(SerdWriter* writer, | |||
| const uint8_t* prefix); | |||
| /** | |||
| Set the current output base URI (and emit directive if applicable). | |||
| Note this function can be safely casted to SerdBaseSink. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_set_base_uri(SerdWriter* writer, | |||
| const SerdNode* uri); | |||
| /** | |||
| Set the current root URI. | |||
| The root URI should be a prefix of the base URI. The path of the root URI | |||
| is the highest path any relative up-reference can refer to. For example, | |||
| with root <file:///foo/root> and base <file:///foo/root/base>, | |||
| <file:///foo/root> will be written as <../>, but <file:///foo> will be | |||
| written non-relatively as <file:///foo>. If the root is not explicitly set, | |||
| it defaults to the base URI, so no up-references will be created at all. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_set_root_uri(SerdWriter* writer, | |||
| const SerdNode* uri); | |||
| /** | |||
| Set a namespace prefix (and emit directive if applicable). | |||
| Note this function can be safely casted to SerdPrefixSink. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_set_prefix(SerdWriter* writer, | |||
| const SerdNode* name, | |||
| const SerdNode* uri); | |||
| /** | |||
| Write a statement. | |||
| Note this function can be safely casted to SerdStatementSink. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_write_statement(SerdWriter* writer, | |||
| SerdStatementFlags flags, | |||
| const SerdNode* graph, | |||
| const SerdNode* subject, | |||
| const SerdNode* predicate, | |||
| const SerdNode* object, | |||
| const SerdNode* object_datatype, | |||
| const SerdNode* object_lang); | |||
| /** | |||
| Mark the end of an anonymous node's description. | |||
| Note this function can be safely casted to SerdEndSink. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_end_anon(SerdWriter* writer, | |||
| const SerdNode* node); | |||
| /** | |||
| Finish a write. | |||
| */ | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_finish(SerdWriter* writer); | |||
| /** | |||
| @} | |||
| @} | |||
| */ | |||
| #ifdef __cplusplus | |||
| } /* extern "C" */ | |||
| #endif | |||
| #endif /* SERD_SERD_H */ | |||
| @@ -0,0 +1,271 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "serd_internal.h" | |||
| #include <assert.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| typedef struct { | |||
| SerdNode name; | |||
| SerdNode uri; | |||
| } SerdPrefix; | |||
| struct SerdEnvImpl { | |||
| SerdPrefix* prefixes; | |||
| size_t n_prefixes; | |||
| SerdNode base_uri_node; | |||
| SerdURI base_uri; | |||
| }; | |||
| SERD_API | |||
| SerdEnv* | |||
| serd_env_new(const SerdNode* base_uri) | |||
| { | |||
| SerdEnv* env = (SerdEnv*)malloc(sizeof(struct SerdEnvImpl)); | |||
| env->prefixes = NULL; | |||
| env->n_prefixes = 0; | |||
| env->base_uri_node = SERD_NODE_NULL; | |||
| env->base_uri = SERD_URI_NULL; | |||
| if (base_uri) { | |||
| serd_env_set_base_uri(env, base_uri); | |||
| } | |||
| return env; | |||
| } | |||
| SERD_API | |||
| void | |||
| serd_env_free(SerdEnv* env) | |||
| { | |||
| for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
| serd_node_free(&env->prefixes[i].name); | |||
| serd_node_free(&env->prefixes[i].uri); | |||
| } | |||
| free(env->prefixes); | |||
| serd_node_free(&env->base_uri_node); | |||
| free(env); | |||
| } | |||
| SERD_API | |||
| const SerdNode* | |||
| serd_env_get_base_uri(const SerdEnv* env, | |||
| SerdURI* out) | |||
| { | |||
| if (out) { | |||
| *out = env->base_uri; | |||
| } | |||
| return &env->base_uri_node; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_set_base_uri(SerdEnv* env, | |||
| const SerdNode* uri_node) | |||
| { | |||
| // Resolve base URI and create a new node and URI for it | |||
| SerdURI base_uri; | |||
| SerdNode base_uri_node = serd_node_new_uri_from_node( | |||
| uri_node, &env->base_uri, &base_uri); | |||
| if (base_uri_node.buf) { | |||
| // Replace the current base URI | |||
| serd_node_free(&env->base_uri_node); | |||
| env->base_uri_node = base_uri_node; | |||
| env->base_uri = base_uri; | |||
| return SERD_SUCCESS; | |||
| } | |||
| return SERD_ERR_BAD_ARG; | |||
| } | |||
| static inline SerdPrefix* | |||
| serd_env_find(const SerdEnv* env, | |||
| const uint8_t* name, | |||
| size_t name_len) | |||
| { | |||
| for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
| const SerdNode* const prefix_name = &env->prefixes[i].name; | |||
| if (prefix_name->n_bytes == name_len) { | |||
| if (!memcmp(prefix_name->buf, name, name_len)) { | |||
| return &env->prefixes[i]; | |||
| } | |||
| } | |||
| } | |||
| return NULL; | |||
| } | |||
| static void | |||
| serd_env_add(SerdEnv* env, | |||
| const SerdNode* name, | |||
| const SerdNode* uri) | |||
| { | |||
| SerdPrefix* const prefix = serd_env_find(env, name->buf, name->n_bytes); | |||
| if (prefix) { | |||
| SerdNode old_prefix_uri = prefix->uri; | |||
| prefix->uri = serd_node_copy(uri); | |||
| serd_node_free(&old_prefix_uri); | |||
| } else { | |||
| env->prefixes = (SerdPrefix*)realloc( | |||
| env->prefixes, (++env->n_prefixes) * sizeof(SerdPrefix)); | |||
| env->prefixes[env->n_prefixes - 1].name = serd_node_copy(name); | |||
| env->prefixes[env->n_prefixes - 1].uri = serd_node_copy(uri); | |||
| } | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_set_prefix(SerdEnv* env, | |||
| const SerdNode* name, | |||
| const SerdNode* uri_node) | |||
| { | |||
| if (!name->buf || uri_node->type != SERD_URI) { | |||
| return SERD_ERR_BAD_ARG; | |||
| } else if (serd_uri_string_has_scheme(uri_node->buf)) { | |||
| // Set prefix to absolute URI | |||
| serd_env_add(env, name, uri_node); | |||
| } else { | |||
| // Resolve relative URI and create a new node and URI for it | |||
| SerdURI abs_uri; | |||
| SerdNode abs_uri_node = serd_node_new_uri_from_node( | |||
| uri_node, &env->base_uri, &abs_uri); | |||
| // Set prefix to resolved (absolute) URI | |||
| serd_env_add(env, name, &abs_uri_node); | |||
| serd_node_free(&abs_uri_node); | |||
| } | |||
| return SERD_SUCCESS; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_set_prefix_from_strings(SerdEnv* env, | |||
| const uint8_t* name, | |||
| const uint8_t* uri) | |||
| { | |||
| const SerdNode name_node = serd_node_from_string(SERD_LITERAL, name); | |||
| const SerdNode uri_node = serd_node_from_string(SERD_URI, uri); | |||
| return serd_env_set_prefix(env, &name_node, &uri_node); | |||
| } | |||
| static inline bool | |||
| is_nameChar(const uint8_t c) | |||
| { | |||
| return is_alpha(c) || is_digit(c) || c == '_'; | |||
| } | |||
| /** | |||
| Return true iff @c buf is a valid prefixed name suffix. | |||
| TODO: This is more strict than it should be. | |||
| */ | |||
| static inline bool | |||
| is_name(const uint8_t* buf, size_t len) | |||
| { | |||
| for (size_t i = 0; i < len; ++i) { | |||
| if (!is_nameChar(buf[i])) { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| SERD_API | |||
| bool | |||
| serd_env_qualify(const SerdEnv* env, | |||
| const SerdNode* uri, | |||
| SerdNode* prefix_name, | |||
| SerdChunk* suffix) | |||
| { | |||
| for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
| const SerdNode* const prefix_uri = &env->prefixes[i].uri; | |||
| if (uri->n_bytes >= prefix_uri->n_bytes) { | |||
| if (!strncmp((const char*)uri->buf, | |||
| (const char*)prefix_uri->buf, | |||
| prefix_uri->n_bytes)) { | |||
| *prefix_name = env->prefixes[i].name; | |||
| suffix->buf = uri->buf + prefix_uri->n_bytes; | |||
| suffix->len = uri->n_bytes - prefix_uri->n_bytes; | |||
| if (is_name(suffix->buf, suffix->len)) { | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_env_expand(const SerdEnv* env, | |||
| const SerdNode* qname, | |||
| SerdChunk* uri_prefix, | |||
| SerdChunk* uri_suffix) | |||
| { | |||
| const uint8_t* const colon = (const uint8_t*)memchr( | |||
| qname->buf, ':', qname->n_bytes + 1); | |||
| if (!colon) { | |||
| return SERD_ERR_BAD_ARG; // Invalid qname | |||
| } | |||
| const size_t name_len = colon - qname->buf; | |||
| const SerdPrefix* const prefix = serd_env_find(env, qname->buf, name_len); | |||
| if (prefix) { | |||
| uri_prefix->buf = prefix->uri.buf; | |||
| uri_prefix->len = prefix->uri.n_bytes; | |||
| uri_suffix->buf = colon + 1; | |||
| uri_suffix->len = qname->n_bytes - (colon - qname->buf) - 1; | |||
| return SERD_SUCCESS; | |||
| } | |||
| return SERD_ERR_NOT_FOUND; | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_env_expand_node(const SerdEnv* env, | |||
| const SerdNode* node) | |||
| { | |||
| switch (node->type) { | |||
| case SERD_CURIE: { | |||
| SerdChunk prefix; | |||
| SerdChunk suffix; | |||
| if (serd_env_expand(env, node, &prefix, &suffix)) { | |||
| return SERD_NODE_NULL; | |||
| } | |||
| const size_t len = prefix.len + suffix.len; // FIXME: UTF-8? | |||
| uint8_t* buf = (uint8_t*)malloc(len + 1); | |||
| SerdNode ret = { buf, len, len, 0, SERD_URI }; | |||
| snprintf((char*)buf, ret.n_bytes + 1, "%s%s", prefix.buf, suffix.buf); | |||
| return ret; | |||
| } | |||
| case SERD_URI: { | |||
| SerdURI ignored; | |||
| return serd_node_new_uri_from_node(node, &env->base_uri, &ignored); | |||
| } | |||
| default: | |||
| return SERD_NODE_NULL; | |||
| } | |||
| } | |||
| SERD_API | |||
| void | |||
| serd_env_foreach(const SerdEnv* env, | |||
| SerdPrefixSink func, | |||
| void* handle) | |||
| { | |||
| for (size_t i = 0; i < env->n_prefixes; ++i) { | |||
| func(handle, &env->prefixes[i].name, &env->prefixes[i].uri); | |||
| } | |||
| } | |||
| @@ -0,0 +1,347 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "serd_internal.h" | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <math.h> | |||
| #include <float.h> | |||
| #ifdef _WIN32 | |||
| # define isnan(x) _isnan(x) | |||
| # define isinf(x) (!_finite(x)) | |||
| #endif | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_from_string(SerdType type, const uint8_t* buf) | |||
| { | |||
| if (!buf) { | |||
| return SERD_NODE_NULL; | |||
| } | |||
| uint32_t flags = 0; | |||
| size_t buf_n_bytes = 0; | |||
| const size_t buf_n_chars = serd_strlen(buf, &buf_n_bytes, &flags); | |||
| SerdNode ret = { buf, buf_n_bytes, buf_n_chars, flags, type }; | |||
| return ret; | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_copy(const SerdNode* node) | |||
| { | |||
| if (!node || !node->buf) { | |||
| return SERD_NODE_NULL; | |||
| } | |||
| SerdNode copy = *node; | |||
| uint8_t* buf = (uint8_t*)malloc(copy.n_bytes + 1); | |||
| memcpy(buf, node->buf, copy.n_bytes + 1); | |||
| copy.buf = buf; | |||
| return copy; | |||
| } | |||
| SERD_API | |||
| bool | |||
| serd_node_equals(const SerdNode* a, const SerdNode* b) | |||
| { | |||
| return (a == b) | |||
| || (a->type == b->type | |||
| && a->n_bytes == b->n_bytes | |||
| && a->n_chars == b->n_chars | |||
| && ((a->buf == b->buf) || !memcmp((const char*)a->buf, | |||
| (const char*)b->buf, | |||
| a->n_bytes + 1))); | |||
| } | |||
| static size_t | |||
| serd_uri_string_length(const SerdURI* uri) | |||
| { | |||
| size_t len = uri->path_base.len; | |||
| #define ADD_LEN(field, n_delims) \ | |||
| if ((field).len) { len += (field).len + (n_delims); } | |||
| ADD_LEN(uri->path, 1); // + possible leading `/' | |||
| ADD_LEN(uri->scheme, 1); // + trailing `:' | |||
| ADD_LEN(uri->authority, 2); // + leading `//' | |||
| ADD_LEN(uri->query, 1); // + leading `?' | |||
| ADD_LEN(uri->fragment, 1); // + leading `#' | |||
| return len + 2; // + 2 for authority `//' | |||
| } | |||
| static size_t | |||
| string_sink(const void* buf, size_t len, void* stream) | |||
| { | |||
| uint8_t** ptr = (uint8_t**)stream; | |||
| memcpy(*ptr, buf, len); | |||
| *ptr += len; | |||
| return len; | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_uri_from_node(const SerdNode* uri_node, | |||
| const SerdURI* base, | |||
| SerdURI* out) | |||
| { | |||
| return (uri_node->type == SERD_URI) | |||
| ? serd_node_new_uri_from_string(uri_node->buf, base, out) | |||
| : SERD_NODE_NULL; | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_uri_from_string(const uint8_t* str, | |||
| const SerdURI* base, | |||
| SerdURI* out) | |||
| { | |||
| if (!str || str[0] == '\0') { | |||
| return serd_node_new_uri(base, NULL, out); // Empty URI => Base URI | |||
| } | |||
| SerdURI uri; | |||
| serd_uri_parse(str, &uri); | |||
| return serd_node_new_uri(&uri, base, out); // Resolve/Serialise | |||
| } | |||
| static inline bool | |||
| is_uri_path_char(const uint8_t c) | |||
| { | |||
| if (is_alpha(c) || is_digit(c)) { | |||
| return true; | |||
| } | |||
| switch (c) { | |||
| case '-': case '.': case '_': case '~': // unreserved | |||
| case ':': case '@': // pchar | |||
| case '/': // separator | |||
| // sub-delims | |||
| case '!': case '$': case '&': case '\'': case '(': case ')': | |||
| case '*': case '+': case ',': case ';': case '=': | |||
| return true; | |||
| default: | |||
| return false; | |||
| } | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_file_uri(const uint8_t* path, | |||
| const uint8_t* hostname, | |||
| SerdURI* out, | |||
| bool escape) | |||
| { | |||
| const size_t path_len = strlen((const char*)path); | |||
| const size_t hostname_len = hostname ? strlen((const char*)hostname) : 0; | |||
| const bool evil = is_windows_path(path); | |||
| size_t uri_len = 0; | |||
| uint8_t* uri = NULL; | |||
| if (path[0] == '/' || is_windows_path(path)) { | |||
| uri_len = strlen("file://") + hostname_len + evil; | |||
| uri = (uint8_t*)malloc(uri_len + 1); | |||
| snprintf((char*)uri, uri_len + 1, "file://%s%s", | |||
| hostname ? (const char*)hostname : "", | |||
| evil ? "/" : ""); | |||
| } | |||
| SerdChunk chunk = { uri, uri_len }; | |||
| for (size_t i = 0; i < path_len; ++i) { | |||
| if (evil && path[i] == '\\') { | |||
| serd_chunk_sink("/", 1, &chunk); | |||
| } else if (path[i] == '%') { | |||
| serd_chunk_sink("%%", 2, &chunk); | |||
| } else if (!escape || is_uri_path_char(path[i])) { | |||
| serd_chunk_sink(path + i, 1, &chunk); | |||
| } else { | |||
| char escape_str[4] = { '%', 0, 0, 0 }; | |||
| snprintf(escape_str + 1, sizeof(escape_str) - 1, "%X", path[i]); | |||
| serd_chunk_sink(escape_str, 3, &chunk); | |||
| } | |||
| } | |||
| serd_chunk_sink_finish(&chunk); | |||
| if (out) { | |||
| serd_uri_parse(chunk.buf, out); | |||
| } | |||
| return serd_node_from_string(SERD_URI, chunk.buf); | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out) | |||
| { | |||
| SerdURI abs_uri = *uri; | |||
| if (base) { | |||
| serd_uri_resolve(uri, base, &abs_uri); | |||
| } | |||
| const size_t len = serd_uri_string_length(&abs_uri); | |||
| uint8_t* buf = (uint8_t*)malloc(len + 1); | |||
| SerdNode node = { buf, len, len, 0, SERD_URI }; // FIXME: UTF-8 | |||
| uint8_t* ptr = buf; | |||
| const size_t actual_len = serd_uri_serialise(&abs_uri, string_sink, &ptr); | |||
| buf[actual_len] = '\0'; | |||
| node.n_bytes = actual_len; | |||
| node.n_chars = actual_len; | |||
| if (out) { | |||
| serd_uri_parse(buf, out); // TODO: cleverly avoid double parse | |||
| } | |||
| return node; | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_decimal(double d, unsigned frac_digits) | |||
| { | |||
| if (isnan(d) || isinf(d)) { | |||
| return SERD_NODE_NULL; | |||
| } | |||
| const double abs_d = fabs(d); | |||
| const unsigned int_digits = (unsigned)fmax(1.0, ceil(log10(abs_d + 1))); | |||
| char* buf = (char*)calloc(int_digits + frac_digits + 3, 1); | |||
| SerdNode node = { (const uint8_t*)buf, 0, 0, 0, SERD_LITERAL }; | |||
| const double int_part = floor(abs_d); | |||
| // Point s to decimal point location | |||
| char* s = buf + int_digits; | |||
| if (d < 0.0) { | |||
| *buf = '-'; | |||
| ++s; | |||
| } | |||
| // Write integer part (right to left) | |||
| char* t = s - 1; | |||
| uint64_t dec = (uint64_t)int_part; | |||
| do { | |||
| *t-- = '0' + (dec % 10); | |||
| } while ((dec /= 10) > 0); | |||
| *s++ = '.'; | |||
| // Write fractional part (right to left) | |||
| double frac_part = fabs(d - int_part); | |||
| if (frac_part < DBL_EPSILON) { | |||
| *s++ = '0'; | |||
| node.n_bytes = node.n_chars = (s - buf); | |||
| } else { | |||
| uint64_t frac = frac_part * pow(10.0, (int)frac_digits) + 0.5; | |||
| s += frac_digits - 1; | |||
| unsigned i = 0; | |||
| // Skip trailing zeros | |||
| for (; i < frac_digits - 1 && !(frac % 10); ++i, --s, frac /= 10) {} | |||
| node.n_bytes = node.n_chars = (s - buf) + 1; | |||
| // Write digits from last trailing zero to decimal point | |||
| for (; i < frac_digits; ++i) { | |||
| *s-- = '0' + (frac % 10); | |||
| frac /= 10; | |||
| } | |||
| } | |||
| return node; | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_integer(int64_t i) | |||
| { | |||
| int64_t abs_i = (i < 0) ? -i : i; | |||
| const unsigned digits = fmax(1.0, ceil(log10((double)abs_i + 1))); | |||
| char* buf = (char*)calloc(digits + 2, 1); | |||
| SerdNode node = { (const uint8_t*)buf, 0, 0, 0, SERD_LITERAL }; | |||
| // Point s to the end | |||
| char* s = buf + digits - 1; | |||
| if (i < 0) { | |||
| *buf = '-'; | |||
| ++s; | |||
| } | |||
| node.n_bytes = node.n_chars = (s - buf) + 1; | |||
| // Write integer part (right to left) | |||
| do { | |||
| *s-- = '0' + (abs_i % 10); | |||
| } while ((abs_i /= 10) > 0); | |||
| return node; | |||
| } | |||
| /** | |||
| Base64 encoding table. | |||
| @see <a href="http://tools.ietf.org/html/rfc3548#section-3">RFC3986 S3</a>. | |||
| */ | |||
| static const uint8_t b64_map[] = | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |||
| /** | |||
| Encode 3 raw bytes to 4 base64 characters. | |||
| */ | |||
| static inline void | |||
| encode_chunk(uint8_t out[4], const uint8_t in[3], size_t n_in) | |||
| { | |||
| out[0] = b64_map[in[0] >> 2]; | |||
| out[1] = b64_map[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; | |||
| out[2] = ((n_in > 1) | |||
| ? (b64_map[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)]) | |||
| : (uint8_t)'='); | |||
| out[3] = ((n_in > 2) ? b64_map[in[2] & 0x3F] : (uint8_t)'='); | |||
| } | |||
| SERD_API | |||
| SerdNode | |||
| serd_node_new_blob(const void* buf, size_t size, bool wrap_lines) | |||
| { | |||
| const size_t len = ((size + 2) / 3) * 4 + (wrap_lines ? (size / 57) : 0); | |||
| uint8_t* str = (uint8_t*)calloc(1, len + 2); | |||
| SerdNode node = { str, len, len, 0, SERD_LITERAL }; | |||
| for (size_t i = 0, j = 0; i < size; i += 3, j += 4) { | |||
| uint8_t in[4] = { 0, 0, 0, 0 }; | |||
| size_t n_in = MIN(3, size - i); | |||
| memcpy(in, (const uint8_t*)buf + i, n_in); | |||
| if (wrap_lines && i > 0 && (i % 57) == 0) { | |||
| str[j++] = '\n'; | |||
| node.flags |= SERD_HAS_NEWLINE; | |||
| } | |||
| encode_chunk(str + j, in, n_in); | |||
| } | |||
| return node; | |||
| } | |||
| SERD_API | |||
| void | |||
| serd_node_free(SerdNode* node) | |||
| { | |||
| if (node->buf) { | |||
| free((uint8_t*)node->buf); | |||
| node->buf = NULL; | |||
| } | |||
| } | |||
| @@ -0,0 +1,306 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #ifndef SERD_INTERNAL_H | |||
| #define SERD_INTERNAL_H | |||
| #define _POSIX_C_SOURCE 201112L /* for posix_memalign and posix_fadvise */ | |||
| #include <assert.h> | |||
| #include <errno.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "serd/serd.h" | |||
| #include "serd_config.h" | |||
| #if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO) | |||
| # include <fcntl.h> | |||
| #endif | |||
| #define SERD_PAGE_SIZE 4096 | |||
| #ifndef MIN | |||
| # define MIN(a, b) (((a) < (b)) ? (a) : (b)) | |||
| #endif | |||
| #ifndef HAVE_FMAX | |||
| static inline double | |||
| fmax(double a, double b) | |||
| { | |||
| return (a < b) ? b : a; | |||
| } | |||
| #endif | |||
| /* File and Buffer Utilities */ | |||
| static inline FILE* | |||
| serd_fopen(const char* path, const char* mode) | |||
| { | |||
| FILE* fd = fopen((const char*)path, mode); | |||
| if (!fd) { | |||
| fprintf(stderr, "Error opening file %s (%s)\n", path, strerror(errno)); | |||
| return NULL; | |||
| } | |||
| #if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FILENO) | |||
| posix_fadvise(fileno(fd), 0, 0, POSIX_FADV_SEQUENTIAL); | |||
| #endif | |||
| return fd; | |||
| } | |||
| static inline void* | |||
| serd_bufalloc(size_t size) | |||
| { | |||
| #ifdef HAVE_POSIX_MEMALIGN | |||
| void* ptr; | |||
| posix_memalign(&ptr, SERD_PAGE_SIZE, size); | |||
| return ptr; | |||
| #else | |||
| return malloc(size); | |||
| #endif | |||
| } | |||
| /* Stack */ | |||
| /** A dynamic stack in memory. */ | |||
| typedef struct { | |||
| uint8_t* buf; ///< Stack memory | |||
| size_t buf_size; ///< Allocated size of buf (>= size) | |||
| size_t size; ///< Conceptual size of stack in buf | |||
| } SerdStack; | |||
| /** An offset to start the stack at. Note 0 is reserved for NULL. */ | |||
| #define SERD_STACK_BOTTOM sizeof(void*) | |||
| static inline SerdStack | |||
| serd_stack_new(size_t size) | |||
| { | |||
| SerdStack stack; | |||
| stack.buf = (uint8_t*)malloc(size); | |||
| stack.buf_size = size; | |||
| stack.size = SERD_STACK_BOTTOM; | |||
| return stack; | |||
| } | |||
| static inline bool | |||
| serd_stack_is_empty(SerdStack* stack) | |||
| { | |||
| return stack->size <= SERD_STACK_BOTTOM; | |||
| } | |||
| static inline void | |||
| serd_stack_free(SerdStack* stack) | |||
| { | |||
| free(stack->buf); | |||
| stack->buf = NULL; | |||
| stack->buf_size = 0; | |||
| stack->size = 0; | |||
| } | |||
| static inline uint8_t* | |||
| serd_stack_push(SerdStack* stack, size_t n_bytes) | |||
| { | |||
| const size_t new_size = stack->size + n_bytes; | |||
| if (stack->buf_size < new_size) { | |||
| stack->buf_size *= 2; | |||
| stack->buf = (uint8_t*)realloc(stack->buf, stack->buf_size); | |||
| } | |||
| uint8_t* const ret = (stack->buf + stack->size); | |||
| stack->size = new_size; | |||
| return ret; | |||
| } | |||
| static inline void | |||
| serd_stack_pop(SerdStack* stack, size_t n_bytes) | |||
| { | |||
| assert(stack->size >= n_bytes); | |||
| stack->size -= n_bytes; | |||
| } | |||
| /* Bulk Sink */ | |||
| typedef struct SerdBulkSinkImpl { | |||
| SerdSink sink; | |||
| void* stream; | |||
| uint8_t* buf; | |||
| size_t size; | |||
| size_t block_size; | |||
| } SerdBulkSink; | |||
| static inline SerdBulkSink | |||
| serd_bulk_sink_new(SerdSink sink, void* stream, size_t block_size) | |||
| { | |||
| SerdBulkSink bsink; | |||
| bsink.sink = sink; | |||
| bsink.stream = stream; | |||
| bsink.size = 0; | |||
| bsink.block_size = block_size; | |||
| bsink.buf = (uint8_t*)serd_bufalloc(block_size); | |||
| return bsink; | |||
| } | |||
| static inline void | |||
| serd_bulk_sink_flush(SerdBulkSink* bsink) | |||
| { | |||
| if (bsink->size > 0) { | |||
| bsink->sink(bsink->buf, bsink->size, bsink->stream); | |||
| } | |||
| bsink->size = 0; | |||
| } | |||
| static inline void | |||
| serd_bulk_sink_free(SerdBulkSink* bsink) | |||
| { | |||
| serd_bulk_sink_flush(bsink); | |||
| free(bsink->buf); | |||
| bsink->buf = NULL; | |||
| } | |||
| static inline size_t | |||
| serd_bulk_sink_write(const void* buf, size_t len, SerdBulkSink* bsink) | |||
| { | |||
| const size_t orig_len = len; | |||
| while (len) { | |||
| const size_t space = bsink->block_size - bsink->size; | |||
| const size_t n = MIN(space, len); | |||
| // Write as much as possible into the remaining buffer space | |||
| memcpy(bsink->buf + bsink->size, buf, n); | |||
| bsink->size += n; | |||
| buf = (const uint8_t*)buf + n; | |||
| len -= n; | |||
| // Flush page if buffer is full | |||
| if (bsink->size == bsink->block_size) { | |||
| bsink->sink(bsink->buf, bsink->block_size, bsink->stream); | |||
| bsink->size = 0; | |||
| } | |||
| } | |||
| return orig_len; | |||
| } | |||
| /* Character utilities */ | |||
| /** Return true if @a c lies within [min...max] (inclusive) */ | |||
| static inline bool | |||
| in_range(const uint8_t c, const uint8_t min, const uint8_t max) | |||
| { | |||
| return (c >= min && c <= max); | |||
| } | |||
| /** RFC2234: ALPHA := %x41-5A / %x61-7A ; A-Z / a-z */ | |||
| static inline bool | |||
| is_alpha(const uint8_t c) | |||
| { | |||
| return in_range(c, 'A', 'Z') || in_range(c, 'a', 'z'); | |||
| } | |||
| /** RFC2234: DIGIT ::= %x30-39 ; 0-9 */ | |||
| static inline bool | |||
| is_digit(const uint8_t c) | |||
| { | |||
| return in_range(c, '0', '9'); | |||
| } | |||
| static inline bool | |||
| is_space(const char c) | |||
| { | |||
| switch (c) { | |||
| case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': | |||
| return true; | |||
| default: | |||
| return false; | |||
| } | |||
| } | |||
| static inline bool | |||
| is_base64(const uint8_t c) | |||
| { | |||
| return is_alpha(c) || is_digit(c) || c == '+' || c == '/' || c == '='; | |||
| } | |||
| static inline bool | |||
| is_windows_path(const uint8_t* path) | |||
| { | |||
| return is_alpha(path[0]) && (path[1] == ':' || path[1] == '|') | |||
| && (path[2] == '/' || path[2] == '\\'); | |||
| } | |||
| /* URI utilities */ | |||
| static inline bool | |||
| chunk_equals(const SerdChunk* a, const SerdChunk* b) | |||
| { | |||
| return a->len == b->len | |||
| && !strncmp((const char*)a->buf, (const char*)b->buf, a->len); | |||
| } | |||
| static inline size_t | |||
| uri_path_len(const SerdURI* uri) | |||
| { | |||
| return uri->path_base.len + uri->path.len; | |||
| } | |||
| static inline uint8_t | |||
| uri_path_at(const SerdURI* uri, size_t i) | |||
| { | |||
| if (i < uri->path_base.len) { | |||
| return uri->path_base.buf[i]; | |||
| } else { | |||
| return uri->path.buf[i - uri->path_base.len]; | |||
| } | |||
| } | |||
| /** Return true iff @p uri is within the base of @p root */ | |||
| static inline bool | |||
| uri_is_under(const SerdURI* uri, const SerdURI* root) | |||
| { | |||
| if (!root || !uri || !root->scheme.len || | |||
| !chunk_equals(&root->scheme, &uri->scheme) || | |||
| !chunk_equals(&root->authority, &uri->authority)) { | |||
| return false; | |||
| } | |||
| bool differ = false; | |||
| const size_t path_len = uri_path_len(uri); | |||
| const size_t root_len = uri_path_len(root); | |||
| for (size_t i = 0; i < path_len && i < root_len; ++i) { | |||
| if (uri_path_at(uri, i) != uri_path_at(root, i)) { | |||
| differ = true; | |||
| } | |||
| if (differ && uri_path_at(root, i) == '/') { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| /* Error reporting */ | |||
| static inline void | |||
| serd_error(SerdErrorSink error_sink, void* handle, const SerdError* e) | |||
| { | |||
| if (error_sink) { | |||
| error_sink(handle, e); | |||
| } else { | |||
| fprintf(stderr, "error: %s:%u:%u: ", e->filename, e->line, e->col); | |||
| vfprintf(stderr, e->fmt, *e->args); | |||
| } | |||
| } | |||
| #endif // SERD_INTERNAL_H | |||
| @@ -0,0 +1,253 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "serd_internal.h" | |||
| #include <assert.h> | |||
| #include <errno.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| typedef struct { | |||
| SerdEnv* env; | |||
| SerdWriter* writer; | |||
| } State; | |||
| static int | |||
| print_version(void) | |||
| { | |||
| printf("serdi " SERD_VERSION " <http://drobilla.net/software/serd>\n"); | |||
| printf("Copyright 2011-2012 David Robillard <http://drobilla.net>.\n" | |||
| "License: <http://www.opensource.org/licenses/isc>\n" | |||
| "This is free software; you are free to change and redistribute it." | |||
| "\nThere is NO WARRANTY, to the extent permitted by law.\n"); | |||
| return 0; | |||
| } | |||
| static int | |||
| print_usage(const char* name, bool error) | |||
| { | |||
| FILE* const os = error ? stderr : stdout; | |||
| fprintf(os, "Usage: %s [OPTION]... INPUT [BASE_URI]\n", name); | |||
| fprintf(os, "Read and write RDF syntax.\n"); | |||
| fprintf(os, "Use - for INPUT to read from standard input.\n\n"); | |||
| fprintf(os, " -b Fast bulk output for large serialisations.\n"); | |||
| fprintf(os, " -c PREFIX Chop PREFIX from matching blank node IDs.\n"); | |||
| fprintf(os, " -e Eat input one character at a time.\n"); | |||
| fprintf(os, " -f Keep full URIs in input (don't qualify).\n"); | |||
| fprintf(os, " -h Display this help and exit.\n"); | |||
| fprintf(os, " -i SYNTAX Input syntax (`turtle' or `ntriples').\n"); | |||
| fprintf(os, " -o SYNTAX Output syntax (`turtle' or `ntriples').\n"); | |||
| fprintf(os, " -p PREFIX Add PREFIX to blank node IDs.\n"); | |||
| fprintf(os, " -q Suppress all output except data.\n"); | |||
| fprintf(os, " -r ROOT_URI Keep relative URIs within ROOT_URI.\n"); | |||
| fprintf(os, " -s INPUT Parse INPUT as string (terminates options).\n"); | |||
| fprintf(os, " -v Display version information and exit.\n"); | |||
| return error ? 1 : 0; | |||
| } | |||
| static bool | |||
| set_syntax(SerdSyntax* syntax, const char* name) | |||
| { | |||
| if (!strcmp(name, "turtle")) { | |||
| *syntax = SERD_TURTLE; | |||
| } else if (!strcmp(name, "ntriples")) { | |||
| *syntax = SERD_NTRIPLES; | |||
| } else { | |||
| fprintf(stderr, "Unknown input format `%s'\n", name); | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| static int | |||
| bad_arg(const char* name, char opt) | |||
| { | |||
| fprintf(stderr, "%s: Bad or missing value for -%c\n", name, opt); | |||
| return 1; | |||
| } | |||
| static SerdStatus | |||
| quiet_error_sink(void* handle, const SerdError* e) | |||
| { | |||
| return SERD_SUCCESS; | |||
| } | |||
| int | |||
| main(int argc, char** argv) | |||
| { | |||
| if (argc < 2) { | |||
| return print_usage(argv[0], true); | |||
| } | |||
| FILE* in_fd = NULL; | |||
| SerdSyntax input_syntax = SERD_TURTLE; | |||
| SerdSyntax output_syntax = SERD_NTRIPLES; | |||
| bool from_file = true; | |||
| bool bulk_read = true; | |||
| bool bulk_write = false; | |||
| bool full_uris = false; | |||
| bool quiet = false; | |||
| const uint8_t* in_name = NULL; | |||
| const uint8_t* add_prefix = NULL; | |||
| const uint8_t* chop_prefix = NULL; | |||
| const uint8_t* root_uri = NULL; | |||
| int a = 1; | |||
| for (; a < argc && argv[a][0] == '-'; ++a) { | |||
| if (argv[a][1] == '\0') { | |||
| in_name = (const uint8_t*)"(stdin)"; | |||
| in_fd = stdin; | |||
| break; | |||
| } else if (argv[a][1] == 'b') { | |||
| bulk_write = true; | |||
| } else if (argv[a][1] == 'e') { | |||
| bulk_read = false; | |||
| } else if (argv[a][1] == 'f') { | |||
| full_uris = true; | |||
| } else if (argv[a][1] == 'h') { | |||
| return print_usage(argv[0], false); | |||
| } else if (argv[a][1] == 'q') { | |||
| quiet = true; | |||
| } else if (argv[a][1] == 'v') { | |||
| return print_version(); | |||
| } else if (argv[a][1] == 's') { | |||
| in_name = (const uint8_t*)"(string)"; | |||
| from_file = false; | |||
| ++a; | |||
| break; | |||
| } else if (argv[a][1] == 'i') { | |||
| if (++a == argc || !set_syntax(&input_syntax, argv[a])) { | |||
| return bad_arg(argv[0], 'i'); | |||
| } | |||
| } else if (argv[a][1] == 'o') { | |||
| if (++a == argc || !set_syntax(&output_syntax, argv[a])) { | |||
| return bad_arg(argv[0], 'o'); | |||
| } | |||
| } else if (argv[a][1] == 'p') { | |||
| if (++a == argc) { | |||
| return bad_arg(argv[0], 'p'); | |||
| } | |||
| add_prefix = (const uint8_t*)argv[a]; | |||
| } else if (argv[a][1] == 'c') { | |||
| if (++a == argc) { | |||
| return bad_arg(argv[0], 'c'); | |||
| } | |||
| chop_prefix = (const uint8_t*)argv[a]; | |||
| } else if (argv[a][1] == 'r') { | |||
| if (++a == argc) { | |||
| return bad_arg(argv[0], 'r'); | |||
| } | |||
| root_uri = (const uint8_t*)argv[a]; | |||
| } else { | |||
| fprintf(stderr, "%s: Unknown option `%s'\n", argv[0], argv[a]); | |||
| return print_usage(argv[0], true); | |||
| } | |||
| } | |||
| if (a == argc) { | |||
| fprintf(stderr, "%s: Missing input\n", argv[0]); | |||
| return 1; | |||
| } | |||
| const uint8_t* input = (const uint8_t*)argv[a++]; | |||
| if (from_file) { | |||
| in_name = in_name ? in_name : input; | |||
| if (!in_fd) { | |||
| input = serd_uri_to_path(in_name); | |||
| if (!input || !(in_fd = serd_fopen((const char*)input, "r"))) { | |||
| return 1; | |||
| } | |||
| } | |||
| } | |||
| SerdURI base_uri = SERD_URI_NULL; | |||
| SerdNode base = SERD_NODE_NULL; | |||
| if (a < argc) { // Base URI given on command line | |||
| base = serd_node_new_uri_from_string( | |||
| (const uint8_t*)argv[a], NULL, &base_uri); | |||
| } else if (from_file && in_fd != stdin) { // Use input file URI | |||
| base = serd_node_new_file_uri(input, NULL, &base_uri, false); | |||
| } | |||
| FILE* out_fd = stdout; | |||
| SerdEnv* env = serd_env_new(&base); | |||
| int output_style = 0; | |||
| if (output_syntax == SERD_NTRIPLES) { | |||
| output_style |= SERD_STYLE_ASCII; | |||
| } else { | |||
| output_style |= SERD_STYLE_ABBREVIATED; | |||
| if (!full_uris) { | |||
| output_style |= SERD_STYLE_CURIED; | |||
| } | |||
| } | |||
| if (input_syntax != SERD_NTRIPLES // Base URI may change (@base) | |||
| || (output_syntax == SERD_TURTLE)) { | |||
| output_style |= SERD_STYLE_RESOLVED; | |||
| } | |||
| if (bulk_write) { | |||
| output_style |= SERD_STYLE_BULK; | |||
| } | |||
| SerdWriter* writer = serd_writer_new( | |||
| output_syntax, (SerdStyle)output_style, | |||
| env, &base_uri, serd_file_sink, out_fd); | |||
| SerdReader* reader = serd_reader_new( | |||
| input_syntax, writer, NULL, | |||
| (SerdBaseSink)serd_writer_set_base_uri, | |||
| (SerdPrefixSink)serd_writer_set_prefix, | |||
| (SerdStatementSink)serd_writer_write_statement, | |||
| (SerdEndSink)serd_writer_end_anon); | |||
| if (quiet) { | |||
| serd_reader_set_error_sink(reader, quiet_error_sink, NULL); | |||
| serd_writer_set_error_sink(writer, quiet_error_sink, NULL); | |||
| } | |||
| SerdNode root = serd_node_from_string(SERD_URI, root_uri); | |||
| serd_writer_set_root_uri(writer, &root); | |||
| serd_writer_chop_blank_prefix(writer, chop_prefix); | |||
| serd_reader_add_blank_prefix(reader, add_prefix); | |||
| SerdStatus status = SERD_SUCCESS; | |||
| if (!from_file) { | |||
| status = serd_reader_read_string(reader, input); | |||
| } else if (bulk_read) { | |||
| status = serd_reader_read_file_handle(reader, in_fd, in_name); | |||
| } else { | |||
| status = serd_reader_start_stream(reader, in_fd, in_name, false); | |||
| while (!status) { | |||
| status = serd_reader_read_chunk(reader); | |||
| } | |||
| serd_reader_end_stream(reader); | |||
| } | |||
| serd_reader_free(reader); | |||
| if (from_file) { | |||
| fclose(in_fd); | |||
| } | |||
| serd_writer_finish(writer); | |||
| serd_writer_free(writer); | |||
| serd_env_free(env); | |||
| serd_node_free(&base); | |||
| return (status > SERD_FAILURE) ? 1 : 0; | |||
| } | |||
| @@ -0,0 +1,165 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "serd_internal.h" | |||
| #include <math.h> | |||
| SERD_API | |||
| const uint8_t* | |||
| serd_strerror(SerdStatus st) | |||
| { | |||
| switch (st) { | |||
| case SERD_SUCCESS: return (const uint8_t*)"Success"; | |||
| case SERD_FAILURE: return (const uint8_t*)"Non-fatal failure"; | |||
| case SERD_ERR_UNKNOWN: return (const uint8_t*)"Unknown error"; | |||
| case SERD_ERR_BAD_SYNTAX: return (const uint8_t*)"Invalid syntax"; | |||
| case SERD_ERR_BAD_ARG: return (const uint8_t*)"Invalid argument"; | |||
| case SERD_ERR_NOT_FOUND: return (const uint8_t*)"Not found"; | |||
| case SERD_ERR_ID_CLASH: return (const uint8_t*)"Blank node ID clash"; | |||
| case SERD_ERR_BAD_CURIE: return (const uint8_t*)"Invalid CURIE"; | |||
| case SERD_ERR_INTERNAL: return (const uint8_t*)"Internal error"; | |||
| } | |||
| return (const uint8_t*)"Unknown error"; // never reached | |||
| } | |||
| SERD_API | |||
| size_t | |||
| serd_strlen(const uint8_t* str, size_t* n_bytes, SerdNodeFlags* flags) | |||
| { | |||
| size_t n_chars = 0; | |||
| size_t i = 0; | |||
| *flags = 0; | |||
| for (; str[i]; ++i) { | |||
| if ((str[i] & 0xC0) != 0x80) { | |||
| // Does not start with `10', start of a new character | |||
| ++n_chars; | |||
| switch (str[i]) { | |||
| case '\r': case '\n': | |||
| *flags |= SERD_HAS_NEWLINE; | |||
| break; | |||
| case '"': | |||
| *flags |= SERD_HAS_QUOTE; | |||
| } | |||
| } | |||
| } | |||
| if (n_bytes) { | |||
| *n_bytes = i; | |||
| } | |||
| return n_chars; | |||
| } | |||
| static inline double | |||
| read_sign(const char** sptr) | |||
| { | |||
| double sign = 1.0; | |||
| switch (**sptr) { | |||
| case '-': sign = -1.0; | |||
| case '+': ++(*sptr); | |||
| default: return sign; | |||
| } | |||
| } | |||
| SERD_API | |||
| double | |||
| serd_strtod(const char* str, char** endptr) | |||
| { | |||
| double result = 0.0; | |||
| // Point s at the first non-whitespace character | |||
| const char* s = str; | |||
| while (is_space(*s)) { ++s; } | |||
| // Read leading sign if necessary | |||
| const double sign = read_sign(&s); | |||
| // Parse integer part | |||
| for (; is_digit(*s); ++s) { | |||
| result = (result * 10.0) + (*s - '0'); | |||
| } | |||
| // Parse fractional part | |||
| if (*s == '.') { | |||
| double denom = 10.0; | |||
| for (++s; is_digit(*s); ++s) { | |||
| result += (*s - '0') / denom; | |||
| denom *= 10.0; | |||
| } | |||
| } | |||
| // Parse exponent | |||
| if (*s == 'e' || *s == 'E') { | |||
| ++s; | |||
| double expt = 0.0; | |||
| double expt_sign = read_sign(&s); | |||
| for (; is_digit(*s); ++s) { | |||
| expt = (expt * 10.0) + (*s - '0'); | |||
| } | |||
| result *= pow(10, expt * expt_sign); | |||
| } | |||
| if (endptr) { | |||
| *endptr = (char*)s; | |||
| } | |||
| return result * sign; | |||
| } | |||
| /** | |||
| Base64 decoding table. | |||
| This is indexed by encoded characters and returns the numeric value used | |||
| for decoding, shifted up by 47 to be in the range of printable ASCII. | |||
| A '$' is a placeholder for characters not in the base64 alphabet. | |||
| */ | |||
| static const char b64_unmap[] = | |||
| "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$" | |||
| "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$" | |||
| "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" | |||
| "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"; | |||
| static inline uint8_t unmap(const uint8_t in) { return b64_unmap[in] - 47; } | |||
| /** | |||
| Decode 4 base64 characters to 3 raw bytes. | |||
| */ | |||
| static inline size_t | |||
| decode_chunk(const uint8_t in[4], uint8_t out[3]) | |||
| { | |||
| out[0] = (uint8_t)(((unmap(in[0]) << 2)) | unmap(in[1]) >> 4); | |||
| out[1] = (uint8_t)(((unmap(in[1]) << 4) & 0xF0) | unmap(in[2]) >> 2); | |||
| out[2] = (uint8_t)(((unmap(in[2]) << 6) & 0xC0) | unmap(in[3])); | |||
| return 1 + (in[2] != '=') + ((in[2] != '=') && (in[3] != '=')); | |||
| } | |||
| SERD_API | |||
| void* | |||
| serd_base64_decode(const uint8_t* str, size_t len, size_t* size) | |||
| { | |||
| void* buf = malloc((len * 3) / 4 + 2); | |||
| *size = 0; | |||
| for (size_t i = 0, j = 0; i < len; j += 3) { | |||
| uint8_t in[] = "===="; | |||
| size_t n_in = 0; | |||
| for (; i < len && n_in < 4; ++n_in) { | |||
| for (; i < len && !is_base64(str[i]); ++i) {} // Skip junk | |||
| in[n_in] = str[i++]; | |||
| } | |||
| if (n_in > 1) { | |||
| *size += decode_chunk(in, (uint8_t*)buf + j); | |||
| } | |||
| } | |||
| return buf; | |||
| } | |||
| @@ -0,0 +1,504 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "serd_internal.h" | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| // #define URI_DEBUG 1 | |||
| SERD_API | |||
| const uint8_t* | |||
| serd_uri_to_path(const uint8_t* uri) | |||
| { | |||
| const uint8_t* path = uri; | |||
| if (!is_windows_path(uri) && serd_uri_string_has_scheme(uri)) { | |||
| if (strncmp((const char*)uri, "file:", 5)) { | |||
| fprintf(stderr, "Non-file URI `%s'\n", uri); | |||
| return NULL; | |||
| } else if (!strncmp((const char*)uri, "file://localhost/", 17)) { | |||
| path = uri + 16; | |||
| } else if (!strncmp((const char*)uri, "file://", 7)) { | |||
| path = uri + 7; | |||
| } else { | |||
| fprintf(stderr, "Invalid file URI `%s'\n", uri); | |||
| return NULL; | |||
| } | |||
| if (is_windows_path(path + 1)) { | |||
| ++path; // Special case for terrible Windows file URIs | |||
| } | |||
| } | |||
| return path; | |||
| } | |||
| SERD_API | |||
| uint8_t* | |||
| serd_file_uri_parse(const uint8_t* uri, uint8_t** hostname) | |||
| { | |||
| const uint8_t* path = uri; | |||
| if (hostname) { | |||
| *hostname = NULL; | |||
| } | |||
| if (!strncmp((const char*)uri, "file://", 7)) { | |||
| const uint8_t* auth = uri + 7; | |||
| if (*auth == '/') { // No hostname | |||
| path = auth; | |||
| } else { // Has hostname | |||
| if (!(path = (const uint8_t*)strchr((const char*)auth, '/'))) { | |||
| return NULL; | |||
| } | |||
| if (hostname) { | |||
| *hostname = (uint8_t*)calloc(1, path - auth + 1); | |||
| memcpy(*hostname, auth, path - auth); | |||
| } | |||
| } | |||
| } | |||
| if (is_windows_path(path + 1)) { | |||
| ++path; | |||
| } | |||
| SerdChunk chunk = { NULL, 0 }; | |||
| for (const uint8_t* s = path; *s; ++s) { | |||
| if (*s == '%') { | |||
| if (*(s + 1) == '%') { | |||
| serd_chunk_sink("%", 1, &chunk); | |||
| ++s; | |||
| } else if (is_digit(*(s + 1)) && is_digit(*(s + 2))) { | |||
| const uint8_t code[3] = { *(s + 1), *(s + 2), 0 }; | |||
| uint32_t num; | |||
| sscanf((const char*)code, "%X", &num); | |||
| const uint8_t c = num; | |||
| serd_chunk_sink(&c, 1, &chunk); | |||
| s += 2; | |||
| } else { | |||
| s += 2; // Junk escape, ignore | |||
| } | |||
| } else { | |||
| serd_chunk_sink(s, 1, &chunk); | |||
| } | |||
| } | |||
| return serd_chunk_sink_finish(&chunk); | |||
| } | |||
| SERD_API | |||
| bool | |||
| serd_uri_string_has_scheme(const uint8_t* utf8) | |||
| { | |||
| // RFC3986: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) | |||
| if (!is_alpha(utf8[0])) { | |||
| return false; // Invalid scheme initial character, URI is relative | |||
| } | |||
| for (uint8_t c; (c = *++utf8) != '\0';) { | |||
| switch (c) { | |||
| case ':': | |||
| return true; // End of scheme | |||
| case '+': case '-': case '.': | |||
| break; // Valid scheme character, continue | |||
| default: | |||
| if (!is_alpha(c) && !is_digit(c)) { | |||
| return false; // Invalid scheme character | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| #ifdef URI_DEBUG | |||
| static void | |||
| serd_uri_dump(const SerdURI* uri, FILE* file) | |||
| { | |||
| #define PRINT_PART(range, name) \ | |||
| if (range.buf) { \ | |||
| fprintf(stderr, " " name " = "); \ | |||
| fwrite((range).buf, 1, (range).len, stderr); \ | |||
| fprintf(stderr, "\n"); \ | |||
| } | |||
| PRINT_PART(uri->scheme, "scheme "); | |||
| PRINT_PART(uri->authority, "authority"); | |||
| PRINT_PART(uri->path_base, "path_base"); | |||
| PRINT_PART(uri->path, "path "); | |||
| PRINT_PART(uri->query, "query "); | |||
| PRINT_PART(uri->fragment, "fragment "); | |||
| } | |||
| #endif | |||
| SERD_API | |||
| SerdStatus | |||
| serd_uri_parse(const uint8_t* utf8, SerdURI* uri) | |||
| { | |||
| *uri = SERD_URI_NULL; | |||
| const uint8_t* ptr = utf8; | |||
| /* See http://tools.ietf.org/html/rfc3986#section-3 | |||
| URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] | |||
| */ | |||
| /* S3.1: scheme ::= ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ | |||
| if (is_alpha(*ptr)) { | |||
| for (uint8_t c = *++ptr; true; c = *++ptr) { | |||
| switch (c) { | |||
| case '\0': case '/': case '?': case '#': | |||
| ptr = utf8; | |||
| goto path; // Relative URI (starts with path by definition) | |||
| case ':': | |||
| uri->scheme.buf = utf8; | |||
| uri->scheme.len = (ptr++) - utf8; | |||
| goto maybe_authority; // URI with scheme | |||
| case '+': case '-': case '.': | |||
| continue; | |||
| default: | |||
| if (is_alpha(c) || is_digit(c)) { | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /* S3.2: The authority component is preceded by a double slash ("//") | |||
| and is terminated by the next slash ("/"), question mark ("?"), | |||
| or number sign ("#") character, or by the end of the URI. | |||
| */ | |||
| maybe_authority: | |||
| if (*ptr == '/' && *(ptr + 1) == '/') { | |||
| ptr += 2; | |||
| uri->authority.buf = ptr; | |||
| for (uint8_t c; (c = *ptr) != '\0'; ++ptr) { | |||
| switch (c) { | |||
| case '/': goto path; | |||
| case '?': goto query; | |||
| case '#': goto fragment; | |||
| default: | |||
| ++uri->authority.len; | |||
| } | |||
| } | |||
| } | |||
| /* RFC3986 S3.3: The path is terminated by the first question mark ("?") | |||
| or number sign ("#") character, or by the end of the URI. | |||
| */ | |||
| path: | |||
| switch (*ptr) { | |||
| case '?': goto query; | |||
| case '#': goto fragment; | |||
| case '\0': goto end; | |||
| default: break; | |||
| } | |||
| uri->path.buf = ptr; | |||
| uri->path.len = 0; | |||
| for (uint8_t c; (c = *ptr) != '\0'; ++ptr) { | |||
| switch (c) { | |||
| case '?': goto query; | |||
| case '#': goto fragment; | |||
| default: | |||
| ++uri->path.len; | |||
| } | |||
| } | |||
| /* RFC3986 S3.4: The query component is indicated by the first question | |||
| mark ("?") character and terminated by a number sign ("#") character | |||
| or by the end of the URI. | |||
| */ | |||
| query: | |||
| if (*ptr == '?') { | |||
| uri->query.buf = ++ptr; | |||
| for (uint8_t c; (c = *ptr) != '\0'; ++ptr) { | |||
| switch (c) { | |||
| case '#': | |||
| goto fragment; | |||
| default: | |||
| ++uri->query.len; | |||
| } | |||
| } | |||
| } | |||
| /* RFC3986 S3.5: A fragment identifier component is indicated by the | |||
| presence of a number sign ("#") character and terminated by the end | |||
| of the URI. | |||
| */ | |||
| fragment: | |||
| if (*ptr == '#') { | |||
| uri->fragment.buf = ptr; | |||
| while (*ptr++ != '\0') { | |||
| ++uri->fragment.len; | |||
| } | |||
| } | |||
| end: | |||
| #ifdef URI_DEBUG | |||
| fprintf(stderr, "PARSE URI <%s>\n", utf8); | |||
| serd_uri_dump(uri, stderr); | |||
| fprintf(stderr, "\n"); | |||
| #endif | |||
| return SERD_SUCCESS; | |||
| } | |||
| /** | |||
| Remove leading dot components from @c path. | |||
| See http://tools.ietf.org/html/rfc3986#section-5.2.3 | |||
| @param up Set to the number of up-references (e.g. "../") trimmed | |||
| @return A pointer to the new start of @path | |||
| */ | |||
| static const uint8_t* | |||
| remove_dot_segments(const uint8_t* path, size_t len, size_t* up) | |||
| { | |||
| const uint8_t* begin = path; | |||
| const uint8_t* const end = path + len; | |||
| *up = 0; | |||
| while (begin < end) { | |||
| switch (begin[0]) { | |||
| case '.': | |||
| switch (begin[1]) { | |||
| case '/': | |||
| begin += 2; // Chop leading "./" | |||
| break; | |||
| case '.': | |||
| switch (begin[2]) { | |||
| case '\0': | |||
| ++*up; | |||
| begin += 2; // Chop input ".." | |||
| break; | |||
| case '/': | |||
| ++*up; | |||
| begin += 3; // Chop leading "../" | |||
| break; | |||
| default: | |||
| return begin; | |||
| } | |||
| break; | |||
| case '\0': | |||
| ++begin; // Chop input "." (and fall-through) | |||
| default: | |||
| return begin; | |||
| } | |||
| break; | |||
| case '/': | |||
| switch (begin[1]) { | |||
| case '.': | |||
| switch (begin[2]) { | |||
| case '/': | |||
| begin += 2; // Leading "/./" => "/" | |||
| break; | |||
| case '.': | |||
| switch (begin[3]) { | |||
| case '/': | |||
| ++*up; | |||
| begin += 3; // Leading "/../" => "/" | |||
| } | |||
| break; | |||
| default: | |||
| return begin; | |||
| } | |||
| } // else fall through | |||
| default: | |||
| return begin; // Finished chopping dot components | |||
| } | |||
| } | |||
| return begin; | |||
| } | |||
| /// Merge @p base and @p path in-place | |||
| static void | |||
| merge(SerdChunk* base, SerdChunk* path) | |||
| { | |||
| size_t up; | |||
| const uint8_t* begin = remove_dot_segments(path->buf, path->len, &up); | |||
| const uint8_t* end = path->buf + path->len; | |||
| if (base->buf && base->len > 0) { | |||
| // Find the up'th last slash | |||
| const uint8_t* base_last = (base->buf + base->len - 1); | |||
| ++up; | |||
| do { | |||
| if (*base_last == '/') { | |||
| --up; | |||
| } | |||
| } while (up > 0 && (--base_last > base->buf)); | |||
| // Set path prefix | |||
| base->len = base_last - base->buf + 1; | |||
| } | |||
| // Set path suffix | |||
| path->buf = begin; | |||
| path->len = end - begin; | |||
| } | |||
| /// See http://tools.ietf.org/html/rfc3986#section-5.2.2 | |||
| SERD_API | |||
| void | |||
| serd_uri_resolve(const SerdURI* r, const SerdURI* base, SerdURI* t) | |||
| { | |||
| if (!base->scheme.len) { | |||
| *t = *r; // Don't resolve against non-absolute URIs | |||
| return; | |||
| } | |||
| t->path_base.buf = NULL; | |||
| t->path_base.len = 0; | |||
| if (r->scheme.len) { | |||
| *t = *r; | |||
| } else { | |||
| if (r->authority.len) { | |||
| t->authority = r->authority; | |||
| t->path = r->path; | |||
| t->query = r->query; | |||
| } else { | |||
| t->path = r->path; | |||
| if (!r->path.len) { | |||
| t->path_base = base->path; | |||
| if (r->query.len) { | |||
| t->query = r->query; | |||
| } else { | |||
| t->query = base->query; | |||
| } | |||
| } else { | |||
| if (r->path.buf[0] != '/') { | |||
| t->path_base = base->path; | |||
| } | |||
| merge(&t->path_base, &t->path); | |||
| t->query = r->query; | |||
| } | |||
| t->authority = base->authority; | |||
| } | |||
| t->scheme = base->scheme; | |||
| t->fragment = r->fragment; | |||
| } | |||
| #ifdef URI_DEBUG | |||
| fprintf(stderr, "## RESOLVE URI\n# BASE\n"); | |||
| serd_uri_dump(base, stderr); | |||
| fprintf(stderr, "# URI\n"); | |||
| serd_uri_dump(r, stderr); | |||
| fprintf(stderr, "# RESULT\n"); | |||
| serd_uri_dump(t, stderr); | |||
| fprintf(stderr, "\n"); | |||
| #endif | |||
| } | |||
| /** Write the path of @p uri starting at index @p i */ | |||
| static size_t | |||
| write_path_tail(SerdSink sink, void* stream, const SerdURI* uri, size_t i) | |||
| { | |||
| size_t len = 0; | |||
| if (i < uri->path_base.len) { | |||
| len += sink(uri->path_base.buf + i, uri->path_base.len - i, stream); | |||
| } | |||
| if (uri->path.buf) { | |||
| if (i < uri->path_base.len) { | |||
| len += sink(uri->path.buf, uri->path.len, stream); | |||
| } else { | |||
| const size_t j = (i - uri->path_base.len); | |||
| len += sink(uri->path.buf + j, uri->path.len - j, stream); | |||
| } | |||
| } | |||
| return len; | |||
| } | |||
| /** Write the path of @p uri relative to the path of @p base. */ | |||
| static size_t | |||
| write_rel_path(SerdSink sink, | |||
| void* stream, | |||
| const SerdURI* uri, | |||
| const SerdURI* base) | |||
| { | |||
| const size_t path_len = uri_path_len(uri); | |||
| const size_t base_len = uri_path_len(base); | |||
| const size_t min_len = (path_len < base_len) ? path_len : base_len; | |||
| // Find the last separator common to both paths | |||
| size_t last_shared_sep = 0; | |||
| size_t i = 0; | |||
| for (; i < min_len && uri_path_at(uri, i) == uri_path_at(base, i); ++i) { | |||
| if (uri_path_at(uri, i) == '/') { | |||
| last_shared_sep = i; | |||
| } | |||
| } | |||
| if (i == path_len && i == base_len) { // Paths are identical | |||
| return 0; | |||
| } else if (last_shared_sep == 0) { // No common components | |||
| return write_path_tail(sink, stream, uri, 0); | |||
| } | |||
| // Find the number of up references ("..") required | |||
| size_t up = 0; | |||
| for (size_t s = last_shared_sep + 1; s < base_len; ++s) { | |||
| if (uri_path_at(base, s) == '/') { | |||
| ++up; | |||
| } | |||
| } | |||
| // Write up references | |||
| size_t len = 0; | |||
| for (size_t u = 0; u < up; ++u) { | |||
| len += sink("../", 3, stream); | |||
| } | |||
| // Write suffix | |||
| return len += write_path_tail(sink, stream, uri, last_shared_sep + 1); | |||
| } | |||
| /// See http://tools.ietf.org/html/rfc3986#section-5.3 | |||
| SERD_API | |||
| size_t | |||
| serd_uri_serialise_relative(const SerdURI* uri, | |||
| const SerdURI* base, | |||
| const SerdURI* root, | |||
| SerdSink sink, | |||
| void* stream) | |||
| { | |||
| size_t len = 0; | |||
| const bool relative = uri_is_under(uri, root ? root : base); | |||
| if (relative) { | |||
| len = write_rel_path(sink, stream, uri, base); | |||
| } | |||
| if (!relative || (!len && base->query.buf)) { | |||
| if (uri->scheme.buf) { | |||
| len += sink(uri->scheme.buf, uri->scheme.len, stream); | |||
| len += sink(":", 1, stream); | |||
| } | |||
| if (uri->authority.buf) { | |||
| len += sink("//", 2, stream); | |||
| len += sink(uri->authority.buf, uri->authority.len, stream); | |||
| } | |||
| len += write_path_tail(sink, stream, uri, 0); | |||
| } | |||
| if (uri->query.buf) { | |||
| len += sink("?", 1, stream); | |||
| len += sink(uri->query.buf, uri->query.len, stream); | |||
| } | |||
| if (uri->fragment.buf) { | |||
| // Note uri->fragment.buf includes the leading `#' | |||
| len += sink(uri->fragment.buf, uri->fragment.len, stream); | |||
| } | |||
| return len; | |||
| } | |||
| /// See http://tools.ietf.org/html/rfc3986#section-5.3 | |||
| SERD_API | |||
| size_t | |||
| serd_uri_serialise(const SerdURI* uri, SerdSink sink, void* stream) | |||
| { | |||
| return serd_uri_serialise_relative(uri, NULL, NULL, sink, stream); | |||
| } | |||
| @@ -0,0 +1,807 @@ | |||
| /* | |||
| Copyright 2011-2012 David Robillard <http://drobilla.net> | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #include "serd_internal.h" | |||
| #include <assert.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
| #define NS_XSD "http://www.w3.org/2001/XMLSchema#" | |||
| typedef struct { | |||
| SerdNode graph; | |||
| SerdNode subject; | |||
| SerdNode predicate; | |||
| } WriteContext; | |||
| static const WriteContext WRITE_CONTEXT_NULL = { | |||
| { 0, 0, 0, 0, SERD_NOTHING }, | |||
| { 0, 0, 0, 0, SERD_NOTHING }, | |||
| { 0, 0, 0, 0, SERD_NOTHING } | |||
| }; | |||
| typedef enum { | |||
| SEP_NONE, | |||
| SEP_END_S, ///< End of a subject ('.') | |||
| SEP_END_P, ///< End of a predicate (';') | |||
| SEP_END_O, ///< End of an object (',') | |||
| SEP_S_P, ///< Between a subject and predicate (whitespace) | |||
| SEP_P_O, ///< Between a predicate and object (whitespace) | |||
| SEP_ANON_BEGIN, ///< Start of anonymous node ('[') | |||
| SEP_ANON_END, ///< End of anonymous node (']') | |||
| SEP_LIST_BEGIN, ///< Start of list ('(') | |||
| SEP_LIST_SEP, ///< List separator (whitespace) | |||
| SEP_LIST_END ///< End of list (')') | |||
| } Sep; | |||
| typedef struct { | |||
| const char* str; ///< Sep string | |||
| uint8_t len; ///< Length of sep string | |||
| uint8_t space_before; ///< Newline before sep | |||
| uint8_t space_after_node; ///< Newline after sep if after node | |||
| uint8_t space_after_sep; ///< Newline after sep if after sep | |||
| } SepRule; | |||
| static const SepRule rules[] = { | |||
| { NULL, 0, 0, 0, 0 }, | |||
| { " .\n\n", 4, 0, 0, 0 }, | |||
| { " ;", 2, 0, 1, 1 }, | |||
| { " ,", 2, 0, 1, 0 }, | |||
| { NULL, 0, 0, 1, 0 }, | |||
| { " ", 1, 0, 0, 0 }, | |||
| { "[", 1, 0, 1, 1 }, | |||
| { "]", 1, 1, 0, 0 }, | |||
| { "(", 1, 0, 0, 0 }, | |||
| { NULL, 1, 0, 1, 0 }, | |||
| { ")", 1, 1, 0, 0 }, | |||
| { "\n", 1, 0, 1, 0 } | |||
| }; | |||
| struct SerdWriterImpl { | |||
| SerdSyntax syntax; | |||
| SerdStyle style; | |||
| SerdEnv* env; | |||
| SerdNode root_node; | |||
| SerdURI root_uri; | |||
| SerdURI base_uri; | |||
| SerdStack anon_stack; | |||
| SerdBulkSink bulk_sink; | |||
| SerdSink sink; | |||
| void* stream; | |||
| SerdErrorSink error_sink; | |||
| void* error_handle; | |||
| WriteContext context; | |||
| SerdNode list_subj; | |||
| unsigned list_depth; | |||
| uint8_t* bprefix; | |||
| size_t bprefix_len; | |||
| unsigned indent; | |||
| Sep last_sep; | |||
| bool empty; | |||
| }; | |||
| typedef enum { | |||
| WRITE_URI, | |||
| WRITE_STRING, | |||
| WRITE_LONG_STRING | |||
| } TextContext; | |||
| static void | |||
| w_err(SerdWriter* writer, SerdStatus st, const char* fmt, ...) | |||
| { | |||
| va_list args; | |||
| va_start(args, fmt); | |||
| const SerdError e = { st, NULL, 0, 0, fmt, &args }; | |||
| serd_error(writer->error_sink, writer->error_handle, &e); | |||
| va_end(args); | |||
| } | |||
| static inline WriteContext* | |||
| anon_stack_top(SerdWriter* writer) | |||
| { | |||
| assert(!serd_stack_is_empty(&writer->anon_stack)); | |||
| return (WriteContext*)(writer->anon_stack.buf | |||
| + writer->anon_stack.size - sizeof(WriteContext)); | |||
| } | |||
| static void | |||
| copy_node(SerdNode* dst, const SerdNode* src) | |||
| { | |||
| if (src) { | |||
| dst->buf = (uint8_t*)realloc((char*)dst->buf, src->n_bytes + 1); | |||
| dst->n_bytes = src->n_bytes; | |||
| dst->n_chars = src->n_chars; | |||
| dst->flags = src->flags; | |||
| dst->type = src->type; | |||
| memcpy((char*)dst->buf, src->buf, src->n_bytes + 1); | |||
| } else { | |||
| dst->type = SERD_NOTHING; | |||
| } | |||
| } | |||
| static inline size_t | |||
| sink(const void* buf, size_t len, SerdWriter* writer) | |||
| { | |||
| if (writer->style & SERD_STYLE_BULK) { | |||
| return serd_bulk_sink_write(buf, len, &writer->bulk_sink); | |||
| } else { | |||
| return writer->sink(buf, len, writer->stream); | |||
| } | |||
| } | |||
| static size_t | |||
| write_text(SerdWriter* writer, TextContext ctx, | |||
| const uint8_t* utf8, size_t n_bytes) | |||
| { | |||
| size_t len = 0; | |||
| char escape[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | |||
| for (size_t i = 0; i < n_bytes;) { | |||
| // Fast bulk write for long strings of printable ASCII | |||
| size_t j = i; | |||
| for (; j < n_bytes; ++j) { | |||
| if (utf8[j] == '>' || utf8[j] == '\\' || utf8[j] == '"' | |||
| || (!in_range(utf8[j], 0x20, 0x7E))) { | |||
| break; | |||
| } | |||
| } | |||
| if (j > i) { | |||
| len += sink(&utf8[i], j - i, writer); | |||
| i = j; | |||
| continue; | |||
| } | |||
| uint8_t in = utf8[i++]; | |||
| if (ctx == WRITE_LONG_STRING) { | |||
| if (in == '\\') { | |||
| len += sink("\\\\", 2, writer); continue; | |||
| } else if (in == '\"' && i == n_bytes) { | |||
| len += sink("\\\"", 2, writer); continue; // '"' at string end | |||
| } | |||
| } else { | |||
| switch (in) { | |||
| case '\\': len += sink("\\\\", 2, writer); continue; | |||
| case '\n': len += sink("\\n", 2, writer); continue; | |||
| case '\r': len += sink("\\r", 2, writer); continue; | |||
| case '\t': len += sink("\\t", 2, writer); continue; | |||
| case '"': | |||
| if (ctx == WRITE_STRING) { | |||
| len += sink("\\\"", 2, writer); | |||
| continue; | |||
| } // else fall-through | |||
| default: break; | |||
| } | |||
| if ((ctx == WRITE_STRING && in == '"') || | |||
| (ctx == WRITE_URI && in == '>')) { | |||
| snprintf(escape, sizeof(escape), "\\u%04X", | |||
| ctx == WRITE_STRING ? '"' : '>'); | |||
| len += sink(escape, 6, writer); | |||
| continue; | |||
| } | |||
| } | |||
| uint32_t c = 0; | |||
| size_t size = 0; | |||
| if ((in & 0x80) == 0) { // Starts with `0' | |||
| c = in & 0x7F; | |||
| if (in_range(c, 0x20, 0x7E) | |||
| || (is_space(c) && ctx == WRITE_LONG_STRING)) { | |||
| len += sink(&in, 1, writer); // Print ASCII character | |||
| } else { | |||
| snprintf(escape, sizeof(escape), "\\u%04X", c); | |||
| len += sink(escape, 6, writer); // ASCII control character | |||
| } | |||
| continue; | |||
| } else if ((in & 0xE0) == 0xC0) { // Starts with `110' | |||
| size = 2; | |||
| c = in & 0x1F; | |||
| } else if ((in & 0xF0) == 0xE0) { // Starts with `1110' | |||
| size = 3; | |||
| c = in & 0x0F; | |||
| } else if ((in & 0xF8) == 0xF0) { // Starts with `11110' | |||
| size = 4; | |||
| c = in & 0x07; | |||
| } else { | |||
| w_err(writer, SERD_ERR_BAD_ARG, "invalid UTF-8: %X\n", in); | |||
| const uint8_t replacement_char[] = { 0xEF, 0xBF, 0xBD }; | |||
| len += sink(replacement_char, sizeof(replacement_char), writer); | |||
| return len; | |||
| } | |||
| if (ctx != WRITE_URI && !(writer->style & SERD_STYLE_ASCII)) { | |||
| // Write UTF-8 character directly to UTF-8 output | |||
| // TODO: Always parse and validate character? | |||
| len += sink(utf8 + i - 1, size, writer); | |||
| i += size - 1; | |||
| continue; | |||
| } | |||
| #define READ_BYTE() \ | |||
| in = utf8[i++] & 0x3f; \ | |||
| c = (c << 6) | in; | |||
| switch (size) { | |||
| case 4: READ_BYTE(); | |||
| case 3: READ_BYTE(); | |||
| case 2: READ_BYTE(); | |||
| } | |||
| if (c < 0xFFFF) { | |||
| snprintf(escape, sizeof(escape), "\\u%04X", c); | |||
| len += sink(escape, 6, writer); | |||
| } else { | |||
| snprintf(escape, sizeof(escape), "\\U%08X", c); | |||
| len += sink(escape, 10, writer); | |||
| } | |||
| } | |||
| return len; | |||
| } | |||
| static size_t | |||
| uri_sink(const void* buf, size_t len, void* stream) | |||
| { | |||
| return write_text((SerdWriter*)stream, WRITE_URI, | |||
| (const uint8_t*)buf, len); | |||
| } | |||
| static void | |||
| write_newline(SerdWriter* writer) | |||
| { | |||
| sink("\n", 1, writer); | |||
| for (unsigned i = 0; i < writer->indent; ++i) { | |||
| sink("\t", 1, writer); | |||
| } | |||
| } | |||
| static void | |||
| write_sep(SerdWriter* writer, const Sep sep) | |||
| { | |||
| const SepRule* rule = &rules[sep]; | |||
| if (rule->space_before) { | |||
| write_newline(writer); | |||
| } | |||
| if (rule->str) { | |||
| sink(rule->str, rule->len, writer); | |||
| } | |||
| if ( (writer->last_sep && rule->space_after_sep) | |||
| || (!writer->last_sep && rule->space_after_node)) { | |||
| write_newline(writer); | |||
| } else if (writer->last_sep && rule->space_after_node) { | |||
| sink(" ", 1, writer); | |||
| } | |||
| writer->last_sep = sep; | |||
| } | |||
| static SerdStatus | |||
| reset_context(SerdWriter* writer, bool del) | |||
| { | |||
| if (del) { | |||
| serd_node_free(&writer->context.graph); | |||
| serd_node_free(&writer->context.subject); | |||
| serd_node_free(&writer->context.predicate); | |||
| writer->context = WRITE_CONTEXT_NULL; | |||
| } else { | |||
| writer->context.graph.type = SERD_NOTHING; | |||
| writer->context.subject.type = SERD_NOTHING; | |||
| writer->context.predicate.type = SERD_NOTHING; | |||
| } | |||
| writer->empty = false; | |||
| return SERD_SUCCESS; | |||
| } | |||
| typedef enum { | |||
| FIELD_NONE, | |||
| FIELD_SUBJECT, | |||
| FIELD_PREDICATE, | |||
| FIELD_OBJECT | |||
| } Field; | |||
| static bool | |||
| write_node(SerdWriter* writer, | |||
| const SerdNode* node, | |||
| const SerdNode* datatype, | |||
| const SerdNode* lang, | |||
| Field field, | |||
| SerdStatementFlags flags) | |||
| { | |||
| SerdChunk uri_prefix; | |||
| SerdChunk uri_suffix; | |||
| bool has_scheme; | |||
| switch (node->type) { | |||
| case SERD_BLANK: | |||
| if (writer->syntax != SERD_NTRIPLES | |||
| && ((field == FIELD_SUBJECT && (flags & SERD_ANON_S_BEGIN)) | |||
| || (field == FIELD_OBJECT && (flags & SERD_ANON_O_BEGIN)))) { | |||
| ++writer->indent; | |||
| write_sep(writer, SEP_ANON_BEGIN); | |||
| } else if (writer->syntax != SERD_NTRIPLES | |||
| && (field == FIELD_SUBJECT && (flags & SERD_LIST_S_BEGIN))) { | |||
| assert(writer->list_depth == 0); | |||
| copy_node(&writer->list_subj, node); | |||
| ++writer->list_depth; | |||
| ++writer->indent; | |||
| write_sep(writer, SEP_LIST_BEGIN); | |||
| } else if (writer->syntax != SERD_NTRIPLES | |||
| && (field == FIELD_OBJECT && (flags & SERD_LIST_O_BEGIN))) { | |||
| ++writer->indent; | |||
| ++writer->list_depth; | |||
| write_sep(writer, SEP_LIST_BEGIN); | |||
| } else if (writer->syntax != SERD_NTRIPLES | |||
| && ((field == FIELD_SUBJECT && (flags & SERD_EMPTY_S)) | |||
| || (field == FIELD_OBJECT && (flags & SERD_EMPTY_O)))) { | |||
| sink("[]", 2, writer); | |||
| } else { | |||
| sink("_:", 2, writer); | |||
| if (writer->bprefix && !strncmp((const char*)node->buf, | |||
| (const char*)writer->bprefix, | |||
| writer->bprefix_len)) { | |||
| sink(node->buf + writer->bprefix_len, | |||
| node->n_bytes - writer->bprefix_len, | |||
| writer); | |||
| } else { | |||
| sink(node->buf, node->n_bytes, writer); | |||
| } | |||
| } | |||
| break; | |||
| case SERD_CURIE: | |||
| switch (writer->syntax) { | |||
| case SERD_NTRIPLES: | |||
| if (serd_env_expand(writer->env, node, &uri_prefix, &uri_suffix)) { | |||
| w_err(writer, SERD_ERR_BAD_CURIE, | |||
| "undefined namespace prefix `%s'\n", node->buf); | |||
| return false; | |||
| } | |||
| sink("<", 1, writer); | |||
| write_text(writer, WRITE_URI, uri_prefix.buf, uri_prefix.len); | |||
| write_text(writer, WRITE_URI, uri_suffix.buf, uri_suffix.len); | |||
| sink(">", 1, writer); | |||
| break; | |||
| case SERD_TURTLE: | |||
| sink(node->buf, node->n_bytes, writer); | |||
| } | |||
| break; | |||
| case SERD_LITERAL: | |||
| if (writer->syntax == SERD_TURTLE && datatype && datatype->buf) { | |||
| const char* type_uri = (const char*)datatype->buf; | |||
| if (!strncmp(type_uri, NS_XSD, sizeof(NS_XSD) - 1) && ( | |||
| !strcmp(type_uri + sizeof(NS_XSD) - 1, "boolean") || | |||
| !strcmp(type_uri + sizeof(NS_XSD) - 1, "decimal") || | |||
| !strcmp(type_uri + sizeof(NS_XSD) - 1, "integer"))) { | |||
| sink(node->buf, node->n_bytes, writer); | |||
| break; | |||
| } | |||
| } | |||
| if (writer->syntax != SERD_NTRIPLES | |||
| && (node->flags & (SERD_HAS_NEWLINE|SERD_HAS_QUOTE))) { | |||
| sink("\"\"\"", 3, writer); | |||
| write_text(writer, WRITE_LONG_STRING, node->buf, node->n_bytes); | |||
| sink("\"\"\"", 3, writer); | |||
| } else { | |||
| sink("\"", 1, writer); | |||
| write_text(writer, WRITE_STRING, node->buf, node->n_bytes); | |||
| sink("\"", 1, writer); | |||
| } | |||
| if (lang && lang->buf) { | |||
| sink("@", 1, writer); | |||
| sink(lang->buf, lang->n_bytes, writer); | |||
| } else if (datatype && datatype->buf) { | |||
| sink("^^", 2, writer); | |||
| write_node(writer, datatype, NULL, NULL, FIELD_NONE, flags); | |||
| } | |||
| break; | |||
| case SERD_URI: | |||
| has_scheme = serd_uri_string_has_scheme(node->buf); | |||
| if (field == FIELD_PREDICATE && (writer->syntax == SERD_TURTLE) | |||
| && !strcmp((const char*)node->buf, NS_RDF "type")) { | |||
| sink("a", 1, writer); | |||
| break; | |||
| } else if ((writer->syntax == SERD_TURTLE) | |||
| && !strcmp((const char*)node->buf, NS_RDF "nil")) { | |||
| sink("()", 2, writer); | |||
| break; | |||
| } else if (has_scheme && (writer->style & SERD_STYLE_CURIED)) { | |||
| SerdNode prefix; | |||
| SerdChunk suffix; | |||
| if (serd_env_qualify(writer->env, node, &prefix, &suffix)) { | |||
| write_text(writer, WRITE_URI, prefix.buf, prefix.n_bytes); | |||
| sink(":", 1, writer); | |||
| write_text(writer, WRITE_URI, suffix.buf, suffix.len); | |||
| break; | |||
| } | |||
| } | |||
| sink("<", 1, writer); | |||
| if (writer->style & SERD_STYLE_RESOLVED) { | |||
| SerdURI in_base_uri, uri, abs_uri; | |||
| serd_env_get_base_uri(writer->env, &in_base_uri); | |||
| serd_uri_parse(node->buf, &uri); | |||
| serd_uri_resolve(&uri, &in_base_uri, &abs_uri); | |||
| bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri); | |||
| SerdURI* root = rooted ? &writer->root_uri : & writer->base_uri; | |||
| if (!uri_is_under(&abs_uri, root) || | |||
| writer->syntax == SERD_NTRIPLES) { | |||
| serd_uri_serialise(&abs_uri, uri_sink, writer); | |||
| } else { | |||
| serd_uri_serialise_relative( | |||
| &uri, &writer->base_uri, root, uri_sink, writer); | |||
| } | |||
| } else { | |||
| write_text(writer, WRITE_URI, node->buf, node->n_bytes); | |||
| } | |||
| sink(">", 1, writer); | |||
| default: | |||
| break; | |||
| } | |||
| writer->last_sep = SEP_NONE; | |||
| return true; | |||
| } | |||
| static inline bool | |||
| is_resource(const SerdNode* node) | |||
| { | |||
| return node->type > SERD_LITERAL; | |||
| } | |||
| static void | |||
| write_pred(SerdWriter* writer, SerdStatementFlags flags, const SerdNode* pred) | |||
| { | |||
| write_node(writer, pred, NULL, NULL, FIELD_PREDICATE, flags); | |||
| write_sep(writer, SEP_P_O); | |||
| copy_node(&writer->context.predicate, pred); | |||
| } | |||
| static bool | |||
| write_list_obj(SerdWriter* writer, | |||
| SerdStatementFlags flags, | |||
| const SerdNode* predicate, | |||
| const SerdNode* object, | |||
| const SerdNode* datatype, | |||
| const SerdNode* lang) | |||
| { | |||
| if (!strcmp((const char*)object->buf, NS_RDF "nil")) { | |||
| --writer->indent; | |||
| write_sep(writer, SEP_LIST_END); | |||
| return true; | |||
| } else if (!strcmp((const char*)predicate->buf, NS_RDF "first")) { | |||
| write_sep(writer, SEP_LIST_SEP); | |||
| write_node(writer, object, datatype, lang, FIELD_OBJECT, flags); | |||
| } | |||
| return false; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_write_statement(SerdWriter* writer, | |||
| SerdStatementFlags flags, | |||
| const SerdNode* graph, | |||
| const SerdNode* subject, | |||
| const SerdNode* predicate, | |||
| const SerdNode* object, | |||
| const SerdNode* datatype, | |||
| const SerdNode* lang) | |||
| { | |||
| if (!subject || !predicate || !object | |||
| || !subject->buf || !predicate->buf || !object->buf | |||
| || !is_resource(subject) || !is_resource(predicate)) { | |||
| return SERD_ERR_BAD_ARG; | |||
| } | |||
| switch (writer->syntax) { | |||
| case SERD_NTRIPLES: | |||
| write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags); | |||
| sink(" ", 1, writer); | |||
| write_node(writer, predicate, NULL, NULL, FIELD_PREDICATE, flags); | |||
| sink(" ", 1, writer); | |||
| if (!write_node(writer, object, datatype, lang, FIELD_OBJECT, flags)) { | |||
| return SERD_ERR_UNKNOWN; | |||
| } | |||
| sink(" .\n", 3, writer); | |||
| return SERD_SUCCESS; | |||
| default: | |||
| break; | |||
| } | |||
| if ((flags & SERD_LIST_CONT)) { | |||
| if (write_list_obj(writer, flags, predicate, object, datatype, lang)) { | |||
| // Reached end of list | |||
| if (--writer->list_depth == 0 && writer->list_subj.type) { | |||
| reset_context(writer, true); | |||
| writer->context.subject = writer->list_subj; | |||
| writer->list_subj = SERD_NODE_NULL; | |||
| } | |||
| return SERD_SUCCESS; | |||
| } | |||
| } else if (serd_node_equals(subject, &writer->context.subject)) { | |||
| if (serd_node_equals(predicate, &writer->context.predicate)) { | |||
| // Abbreviate S P | |||
| if (!(flags & SERD_ANON_O_BEGIN)) { | |||
| ++writer->indent; | |||
| } | |||
| write_sep(writer, SEP_END_O); | |||
| write_node(writer, object, datatype, lang, FIELD_OBJECT, flags); | |||
| if (!(flags & SERD_ANON_O_BEGIN)) { | |||
| --writer->indent; | |||
| } | |||
| } else { | |||
| // Abbreviate S | |||
| Sep sep = writer->context.predicate.type ? SEP_END_P : SEP_S_P; | |||
| write_sep(writer, sep); | |||
| write_pred(writer, flags, predicate); | |||
| write_node(writer, object, datatype, lang, FIELD_OBJECT, flags); | |||
| } | |||
| } else { | |||
| // No abbreviation | |||
| if (writer->context.subject.type) { | |||
| assert(writer->indent > 0); | |||
| --writer->indent; | |||
| if (serd_stack_is_empty(&writer->anon_stack)) { | |||
| write_sep(writer, SEP_END_S); | |||
| } | |||
| } else if (!writer->empty) { | |||
| write_sep(writer, SEP_S_P); | |||
| } | |||
| if (!(flags & SERD_ANON_CONT)) { | |||
| write_node(writer, subject, NULL, NULL, FIELD_SUBJECT, flags); | |||
| ++writer->indent; | |||
| write_sep(writer, SEP_S_P); | |||
| } else { | |||
| ++writer->indent; | |||
| } | |||
| reset_context(writer, true); | |||
| copy_node(&writer->context.subject, subject); | |||
| if (!(flags & SERD_LIST_S_BEGIN)) { | |||
| write_pred(writer, flags, predicate); | |||
| } | |||
| write_node(writer, object, datatype, lang, FIELD_OBJECT, flags); | |||
| } | |||
| if (flags & (SERD_ANON_S_BEGIN|SERD_ANON_O_BEGIN)) { | |||
| WriteContext* ctx = (WriteContext*)serd_stack_push( | |||
| &writer->anon_stack, sizeof(WriteContext)); | |||
| *ctx = writer->context; | |||
| WriteContext new_context = { | |||
| serd_node_copy(graph), serd_node_copy(subject), SERD_NODE_NULL }; | |||
| if ((flags & SERD_ANON_S_BEGIN)) { | |||
| new_context.predicate = serd_node_copy(predicate); | |||
| } | |||
| writer->context = new_context; | |||
| } else { | |||
| copy_node(&writer->context.graph, graph); | |||
| copy_node(&writer->context.subject, subject); | |||
| copy_node(&writer->context.predicate, predicate); | |||
| } | |||
| return SERD_SUCCESS; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_end_anon(SerdWriter* writer, | |||
| const SerdNode* node) | |||
| { | |||
| if (writer->syntax == SERD_NTRIPLES) { | |||
| return SERD_SUCCESS; | |||
| } | |||
| if (serd_stack_is_empty(&writer->anon_stack)) { | |||
| w_err(writer, SERD_ERR_UNKNOWN, | |||
| "unexpected end of anonymous node\n"); | |||
| return SERD_ERR_UNKNOWN; | |||
| } | |||
| assert(writer->indent > 0); | |||
| --writer->indent; | |||
| write_sep(writer, SEP_ANON_END); | |||
| reset_context(writer, true); | |||
| writer->context = *anon_stack_top(writer); | |||
| serd_stack_pop(&writer->anon_stack, sizeof(WriteContext)); | |||
| const bool is_subject = serd_node_equals(node, &writer->context.subject); | |||
| if (is_subject) { | |||
| copy_node(&writer->context.subject, node); | |||
| writer->context.predicate.type = SERD_NOTHING; | |||
| } | |||
| return SERD_SUCCESS; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_finish(SerdWriter* writer) | |||
| { | |||
| if (writer->context.subject.type) { | |||
| sink(" .\n", 3, writer); | |||
| } | |||
| if (writer->style & SERD_STYLE_BULK) { | |||
| serd_bulk_sink_flush(&writer->bulk_sink); | |||
| } | |||
| writer->indent = 0; | |||
| return reset_context(writer, true); | |||
| } | |||
| SERD_API | |||
| SerdWriter* | |||
| serd_writer_new(SerdSyntax syntax, | |||
| SerdStyle style, | |||
| SerdEnv* env, | |||
| const SerdURI* base_uri, | |||
| SerdSink ssink, | |||
| void* stream) | |||
| { | |||
| const WriteContext context = WRITE_CONTEXT_NULL; | |||
| SerdWriter* writer = (SerdWriter*)malloc(sizeof(SerdWriter)); | |||
| writer->syntax = syntax; | |||
| writer->style = style; | |||
| writer->env = env; | |||
| writer->root_node = SERD_NODE_NULL; | |||
| writer->root_uri = SERD_URI_NULL; | |||
| writer->base_uri = base_uri ? *base_uri : SERD_URI_NULL; | |||
| writer->anon_stack = serd_stack_new(sizeof(WriteContext)); | |||
| writer->sink = ssink; | |||
| writer->stream = stream; | |||
| writer->error_sink = NULL; | |||
| writer->error_handle = NULL; | |||
| writer->context = context; | |||
| writer->list_subj = SERD_NODE_NULL; | |||
| writer->list_depth = 0; | |||
| writer->bprefix = NULL; | |||
| writer->bprefix_len = 0; | |||
| writer->indent = 0; | |||
| writer->last_sep = SEP_NONE; | |||
| writer->empty = true; | |||
| if (style & SERD_STYLE_BULK) { | |||
| writer->bulk_sink = serd_bulk_sink_new(ssink, stream, SERD_PAGE_SIZE); | |||
| } | |||
| return writer; | |||
| } | |||
| SERD_API | |||
| void | |||
| serd_writer_set_error_sink(SerdWriter* writer, | |||
| SerdErrorSink error_sink, | |||
| void* error_handle) | |||
| { | |||
| writer->error_sink = error_sink; | |||
| writer->error_handle = error_handle; | |||
| } | |||
| SERD_API | |||
| void | |||
| serd_writer_chop_blank_prefix(SerdWriter* writer, | |||
| const uint8_t* prefix) | |||
| { | |||
| free(writer->bprefix); | |||
| writer->bprefix_len = 0; | |||
| writer->bprefix = NULL; | |||
| if (prefix) { | |||
| writer->bprefix_len = strlen((const char*)prefix); | |||
| writer->bprefix = (uint8_t*)malloc(writer->bprefix_len + 1); | |||
| memcpy(writer->bprefix, prefix, writer->bprefix_len + 1); | |||
| } | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_set_base_uri(SerdWriter* writer, | |||
| const SerdNode* uri) | |||
| { | |||
| if (!serd_env_set_base_uri(writer->env, uri)) { | |||
| serd_env_get_base_uri(writer->env, &writer->base_uri); | |||
| if (writer->syntax != SERD_NTRIPLES) { | |||
| if (writer->context.graph.type || writer->context.subject.type) { | |||
| sink(" .\n\n", 4, writer); | |||
| reset_context(writer, false); | |||
| } | |||
| sink("@base <", 7, writer); | |||
| sink(uri->buf, uri->n_bytes, writer); | |||
| sink("> .\n", 4, writer); | |||
| } | |||
| writer->indent = 0; | |||
| return reset_context(writer, false); | |||
| } | |||
| return SERD_ERR_UNKNOWN; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_set_root_uri(SerdWriter* writer, | |||
| const SerdNode* uri) | |||
| { | |||
| serd_node_free(&writer->root_node); | |||
| if (uri && uri->buf) { | |||
| writer->root_node = serd_node_copy(uri); | |||
| serd_uri_parse(uri->buf, &writer->root_uri); | |||
| } else { | |||
| writer->root_node = SERD_NODE_NULL; | |||
| writer->root_uri = SERD_URI_NULL; | |||
| } | |||
| return SERD_SUCCESS; | |||
| } | |||
| SERD_API | |||
| SerdStatus | |||
| serd_writer_set_prefix(SerdWriter* writer, | |||
| const SerdNode* name, | |||
| const SerdNode* uri) | |||
| { | |||
| if (!serd_env_set_prefix(writer->env, name, uri)) { | |||
| if (writer->syntax != SERD_NTRIPLES) { | |||
| if (writer->context.graph.type || writer->context.subject.type) { | |||
| sink(" .\n\n", 4, writer); | |||
| reset_context(writer, false); | |||
| } | |||
| sink("@prefix ", 8, writer); | |||
| sink(name->buf, name->n_bytes, writer); | |||
| sink(": <", 3, writer); | |||
| write_text(writer, WRITE_URI, uri->buf, uri->n_bytes); | |||
| sink("> .\n", 4, writer); | |||
| } | |||
| writer->indent = 0; | |||
| return reset_context(writer, false); | |||
| } | |||
| return SERD_ERR_UNKNOWN; | |||
| } | |||
| SERD_API | |||
| void | |||
| serd_writer_free(SerdWriter* writer) | |||
| { | |||
| serd_writer_finish(writer); | |||
| serd_stack_free(&writer->anon_stack); | |||
| free(writer->bprefix); | |||
| if (writer->style & SERD_STYLE_BULK) { | |||
| serd_bulk_sink_free(&writer->bulk_sink); | |||
| } | |||
| serd_node_free(&writer->root_node); | |||
| free(writer); | |||
| } | |||
| SERD_API | |||
| SerdEnv* | |||
| serd_writer_get_env(SerdWriter* writer) | |||
| { | |||
| return writer->env; | |||
| } | |||
| SERD_API | |||
| size_t | |||
| serd_file_sink(const void* buf, size_t len, void* stream) | |||
| { | |||
| return fwrite(buf, 1, len, (FILE*)stream); | |||
| } | |||
| SERD_API | |||
| size_t | |||
| serd_chunk_sink(const void* buf, size_t len, void* stream) | |||
| { | |||
| SerdChunk* chunk = (SerdChunk*)stream; | |||
| chunk->buf = (uint8_t*)realloc((uint8_t*)chunk->buf, chunk->len + len); | |||
| memcpy((uint8_t*)chunk->buf + chunk->len, buf, len); | |||
| chunk->len += len; | |||
| return len; | |||
| } | |||
| SERD_API | |||
| uint8_t* | |||
| serd_chunk_sink_finish(SerdChunk* stream) | |||
| { | |||
| serd_chunk_sink("", 1, stream); | |||
| return (uint8_t*)stream->buf; | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| These are the tests for the Turtle Terse RDF Triple Language | |||
| that must be passed by conformant systems. See | |||
| http://www.dajobe.org/2004/01/turtle/ | |||
| for the full conformance information. | |||
| The format is a set of good tests and bad tests. | |||
| Good tests are a pair of files: | |||
| abc.ttl abc.out | |||
| which are the input Turtle file and the expected output RDF triples, | |||
| written in N-Triples. | |||
| bad tests are of the form | |||
| bad-XX.ttl | |||
| which must fail. | |||
| The tests should be performed with an assumed base URI | |||
| of http://www.w3.org/2001/sw/DataAccess/df1/tests/ | |||
| Dave | |||
| @@ -0,0 +1,219 @@ | |||
| @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . | |||
| <> rdfs:comment """ | |||
| UTF-8 encoded sample plain-text file | |||
| ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ | |||
| Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25 | |||
| The ASCII compatible UTF-8 encoding used in this plain-text file | |||
| is defined in Unicode, ISO 10646-1, and RFC 2279. | |||
| Using Unicode/UTF-8, you can write in emails and source code things such as | |||
| Mathematics and sciences: | |||
| ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫ | |||
| ⎪⎢⎜│a²+b³ ⎟⎥⎪ | |||
| ∀x∈ℝ: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), ⎪⎢⎜│───── ⎟⎥⎪ | |||
| ⎪⎢⎜⎷ c₈ ⎟⎥⎪ | |||
| ℕ ⊆ ℕ₀ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ, ⎨⎢⎜ ⎟⎥⎬ | |||
| ⎪⎢⎜ ∞ ⎟⎥⎪ | |||
| ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪ | |||
| ⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪ | |||
| 2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭ | |||
| Linguistics and dictionaries: | |||
| ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn | |||
| Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ] | |||
| APL: | |||
| ((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈ | |||
| Nicer typography in plain text files: | |||
| ╔══════════════════════════════════════════╗ | |||
| ║ ║ | |||
| ║ • ‘single’ and “double” quotes ║ | |||
| ║ ║ | |||
| ║ • Curly apostrophes: “We’ve been here” ║ | |||
| ║ ║ | |||
| ║ • Latin-1 apostrophe and accents: '´` ║ | |||
| ║ ║ | |||
| ║ • ‚deutsche‘ „Anführungszeichen“ ║ | |||
| ║ ║ | |||
| ║ • †, ‡, ‰, •, 3–4, —, −5/+5, ™, … ║ | |||
| ║ ║ | |||
| ║ • ASCII safety test: 1lI|, 0OD, 8B ║ | |||
| ║ ╭─────────╮ ║ | |||
| ║ • the euro symbol: │ 14.95 € │ ║ | |||
| ║ ╰─────────╯ ║ | |||
| ╚══════════════════════════════════════════╝ | |||
| Combining characters: | |||
| STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ | |||
| Greek (in Polytonic): | |||
| The Greek anthem: | |||
| Σὲ γνωρίζω ἀπὸ τὴν κόψη | |||
| τοῦ σπαθιοῦ τὴν τρομερή, | |||
| σὲ γνωρίζω ἀπὸ τὴν ὄψη | |||
| ποὺ μὲ βία μετράει τὴ γῆ. | |||
| ᾿Απ᾿ τὰ κόκκαλα βγαλμένη | |||
| τῶν ῾Ελλήνων τὰ ἱερά | |||
| καὶ σὰν πρῶτα ἀνδρειωμένη | |||
| χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά! | |||
| From a speech of Demosthenes in the 4th century BC: | |||
| Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι, | |||
| ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς | |||
| λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ | |||
| τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿ | |||
| εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ | |||
| πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν | |||
| οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι, | |||
| οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν | |||
| ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον | |||
| τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι | |||
| γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν | |||
| προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους | |||
| σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ | |||
| τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ | |||
| τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς | |||
| τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον. | |||
| Δημοσθένους, Γ´ ᾿Ολυνθιακὸς | |||
| Georgian: | |||
| From a Unicode conference invitation: | |||
| გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო | |||
| კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს, | |||
| ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს | |||
| ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი, | |||
| ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება | |||
| ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში, | |||
| ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში. | |||
| Russian: | |||
| From a Unicode conference invitation: | |||
| Зарегистрируйтесь сейчас на Десятую Международную Конференцию по | |||
| Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии. | |||
| Конференция соберет широкий круг экспертов по вопросам глобального | |||
| Интернета и Unicode, локализации и интернационализации, воплощению и | |||
| применению Unicode в различных операционных системах и программных | |||
| приложениях, шрифтах, верстке и многоязычных компьютерных системах. | |||
| Thai (UCS Level 2): | |||
| Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese | |||
| classic 'San Gua'): | |||
| [----------------------------|------------------------] | |||
| ๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่ | |||
| สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา | |||
| ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา | |||
| โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ | |||
| เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ | |||
| ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ | |||
| พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้ | |||
| ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ | |||
| (The above is a two-column text. If combining characters are handled | |||
| correctly, the lines of the second column should be aligned with the | |||
| | character above.) | |||
| Ethiopian: | |||
| Proverbs in the Amharic language: | |||
| ሰማይ አይታረስ ንጉሥ አይከሰስ። | |||
| ብላ ካለኝ እንደአባቴ በቆመጠኝ። | |||
| ጌጥ ያለቤቱ ቁምጥና ነው። | |||
| ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። | |||
| የአፍ ወለምታ በቅቤ አይታሽም። | |||
| አይጥ በበላ ዳዋ ተመታ። | |||
| ሲተረጉሙ ይደረግሙ። | |||
| ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። | |||
| ድር ቢያብር አንበሳ ያስር። | |||
| ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም። | |||
| እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም። | |||
| የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ። | |||
| ሥራ ከመፍታት ልጄን ላፋታት። | |||
| ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል። | |||
| የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ። | |||
| ተንጋሎ ቢተፉ ተመልሶ ባፉ። | |||
| ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው። | |||
| እግርህን በፍራሽህ ልክ ዘርጋ። | |||
| Runes: | |||
| ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ | |||
| (Old English, which transcribed into Latin reads 'He cwaeth that he | |||
| bude thaem lande northweardum with tha Westsae.' and means 'He said | |||
| that he lived in the northern land near the Western Sea.') | |||
| Braille: | |||
| ⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌ | |||
| ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞ | |||
| ⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎ | |||
| ⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂ | |||
| ⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙ | |||
| ⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑ | |||
| ⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲ | |||
| ⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ | |||
| ⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹ | |||
| ⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞ | |||
| ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕ | |||
| ⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹ | |||
| ⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎ | |||
| ⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎ | |||
| ⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳ | |||
| ⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞ | |||
| ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ | |||
| (The first couple of paragraphs of "A Christmas Carol" by Dickens) | |||
| Compact font selection example text: | |||
| ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789 | |||
| abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ | |||
| –—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд | |||
| ∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა | |||
| Greetings in various languages: | |||
| Hello world, Καλημέρα κόσμε, コンニチハ | |||
| Box drawing alignment tests: █ | |||
| ▉ | |||
| ╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳ | |||
| ║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳ | |||
| ║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳ | |||
| ╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳ | |||
| ║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎ | |||
| ║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏ | |||
| ╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█ | |||
| ▝▀▘▙▄▟ | |||
| """ . | |||
| <> rdfs:comment """ | |||
| Two byte Unicode escape: \u00E0 | |||
| Largest Unicode escape in Turtle: \U0010FFFF | |||
| """ . | |||
| @@ -0,0 +1,2 @@ | |||
| # prefix name must end in a : | |||
| @prefix a <#> . | |||
| @@ -0,0 +1,3 @@ | |||
| # Forbidden by RDF - predicate cannot be blank | |||
| @prefix : <http://example.org/base#> . | |||
| :a [ :b :c ] :d . | |||
| @@ -0,0 +1,3 @@ | |||
| # Forbidden by RDF - predicate cannot be blank | |||
| @prefix : <http://example.org/base#> . | |||
| :a [] :b . | |||
| @@ -0,0 +1,3 @@ | |||
| # 'a' only allowed as a predicate | |||
| @prefix : <http://example.org/base#> . | |||
| a :a :b . | |||
| @@ -0,0 +1,3 @@ | |||
| # No comma is allowed in collections | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| :a :b ( "apple", "banana" ) . | |||
| @@ -0,0 +1,4 @@ | |||
| # N3 {}s are not in Turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| { :a :b :c . } :d :e . | |||
| @@ -0,0 +1,3 @@ | |||
| # is and of are not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| :a is :b of :c . | |||
| @@ -0,0 +1,4 @@ | |||
| # paths are not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| :a.:b.:c . | |||
| :a^:b^:c . | |||
| @@ -0,0 +1,2 @@ | |||
| @keywords something. | |||
| # @keywords is not in turtle | |||
| @@ -0,0 +1,3 @@ | |||
| # implies is not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| :a => :b . | |||
| @@ -0,0 +1,3 @@ | |||
| # equivalence is not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| :a = :b . | |||
| @@ -0,0 +1,3 @@ | |||
| # @forAll is not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| @forAll :x . | |||
| @@ -0,0 +1,3 @@ | |||
| # @forSome is not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| @forSome :x . | |||
| @@ -0,0 +1,3 @@ | |||
| # <= is not in turtle | |||
| @prefix : <http://example.org/stuff/1.0/> . | |||
| :a <= :b . | |||