summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--AUTHORS11
-rw-r--r--COPYING38
-rw-r--r--INSTALL66
-rw-r--r--NEWS228
-rw-r--r--README8
-rw-r--r--bindings/lilv.i66
-rw-r--r--bindings/numpy.i1746
-rw-r--r--bindings/python/Makefile186
-rw-r--r--bindings/python/conf.py263
-rw-r--r--bindings/python/index.rst9
-rw-r--r--bindings/python/lilv.py1775
-rwxr-xr-xbindings/python/lv2_apply.py159
-rwxr-xr-xbindings/python/lv2_list.py9
-rw-r--r--bindings/test/bindings_test_plugin.c196
-rw-r--r--bindings/test/bindings_test_plugin.ttl.in62
-rw-r--r--bindings/test/manifest.ttl.in7
-rw-r--r--bindings/test/python/test_api.py290
-rw-r--r--doc/layout.xml187
-rw-r--r--doc/lv2apply.134
-rw-r--r--doc/lv2info.133
-rw-r--r--doc/lv2ls.130
-rw-r--r--doc/reference.doxygen.in2415
-rw-r--r--doc/style.css691
-rw-r--r--lilv.pc.in11
-rw-r--r--lilv.ttl29
-rw-r--r--lilv/lilv.h1844
-rw-r--r--lilv/lilvmm.hpp345
-rw-r--r--src/collections.c224
-rw-r--r--src/instance.c110
-rw-r--r--src/lib.c112
-rw-r--r--src/lilv_internal.h444
-rw-r--r--src/node.c397
-rw-r--r--src/plugin.c1138
-rw-r--r--src/pluginclass.c88
-rw-r--r--src/port.c268
-rw-r--r--src/query.c139
-rw-r--r--src/scalepoint.c49
-rw-r--r--src/state.c1329
-rw-r--r--src/ui.c111
-rw-r--r--src/util.c640
-rw-r--r--src/world.c1212
-rw-r--r--src/zix/common.h88
-rw-r--r--src/zix/tree.c716
-rw-r--r--src/zix/tree.h148
-rw-r--r--test/bad_syntax.lv2/bad_syntax.c93
-rw-r--r--test/bad_syntax.lv2/bad_syntax.ttl.in22
-rw-r--r--test/bad_syntax.lv2/manifest.ttl.in7
-rw-r--r--test/bad_syntax.lv2/test_bad_syntax.c45
-rw-r--r--test/failed_instantiation.lv2/failed_instantiation.c70
-rw-r--r--test/failed_instantiation.lv2/failed_instantiation.ttl.in40
-rw-r--r--test/failed_instantiation.lv2/manifest.ttl.in7
-rw-r--r--test/failed_instantiation.lv2/test_failed_instantiation.c45
-rw-r--r--test/failed_lib_descriptor.lv2/failed_lib_descriptor.c30
-rw-r--r--test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in38
-rw-r--r--test/failed_lib_descriptor.lv2/manifest.ttl.in7
-rw-r--r--test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c46
-rw-r--r--test/lib_descriptor.lv2/lib_descriptor.c112
-rw-r--r--test/lib_descriptor.lv2/lib_descriptor.ttl.in41
-rw-r--r--test/lib_descriptor.lv2/manifest.ttl.in7
-rw-r--r--test/lib_descriptor.lv2/test_lib_descriptor.c59
-rw-r--r--test/lilv_cxx_test.cpp23
-rw-r--r--test/lilv_test.c2296
-rw-r--r--test/missing_descriptor.lv2/manifest.ttl.in7
-rw-r--r--test/missing_descriptor.lv2/missing_descriptor.c21
-rw-r--r--test/missing_descriptor.lv2/missing_descriptor.ttl.in38
-rw-r--r--test/missing_descriptor.lv2/test_missing_descriptor.c46
-rw-r--r--test/missing_name.lv2/manifest.ttl.in7
-rw-r--r--test/missing_name.lv2/missing_name.c93
-rw-r--r--test/missing_name.lv2/missing_name.ttl.in37
-rw-r--r--test/missing_name.lv2/test_missing_name.c47
-rw-r--r--test/missing_plugin.lv2/manifest.ttl.in7
-rw-r--r--test/missing_plugin.lv2/missing_plugin.c43
-rw-r--r--test/missing_plugin.lv2/missing_plugin.ttl.in38
-rw-r--r--test/missing_plugin.lv2/test_missing_plugin.c46
-rw-r--r--test/missing_port.lv2/manifest.ttl.in7
-rw-r--r--test/missing_port.lv2/missing_port.c93
-rw-r--r--test/missing_port.lv2/missing_port.ttl.in31
-rw-r--r--test/missing_port.lv2/test_missing_port.c45
-rw-r--r--test/missing_port_name.lv2/manifest.ttl.in7
-rw-r--r--test/missing_port_name.lv2/missing_port_name.c93
-rw-r--r--test/missing_port_name.lv2/missing_port_name.ttl.in30
-rw-r--r--test/missing_port_name.lv2/test_missing_port_name.c49
-rw-r--r--test/new_version.lv2/manifest.ttl.in7
-rw-r--r--test/new_version.lv2/new_version.c93
-rw-r--r--test/new_version.lv2/new_version.ttl.in40
-rw-r--r--test/old_version.lv2/manifest.ttl.in7
-rw-r--r--test/old_version.lv2/old_version.c93
-rw-r--r--test/old_version.lv2/old_version.ttl.in40
-rw-r--r--test/test.lv2/manifest.ttl.in7
-rw-r--r--test/test.lv2/test.c394
-rw-r--r--test/test.lv2/test.ttl.in46
-rw-r--r--utils/bench.h52
-rw-r--r--utils/lilv-bench.c38
-rw-r--r--utils/lilv.bash_completion59
-rw-r--r--utils/lv2apply.c354
-rw-r--r--utils/lv2bench.c268
-rw-r--r--utils/lv2info.c460
-rw-r--r--utils/lv2ls.c93
-rw-r--r--utils/uri_table.h73
-rwxr-xr-xwaf171
-rw-r--r--waflib/.gitignore (renamed from .gitignore)0
-rw-r--r--waflib/Build.py (renamed from Build.py)0
-rw-r--r--waflib/COPYING25
-rw-r--r--waflib/ConfigSet.py (renamed from ConfigSet.py)0
-rw-r--r--waflib/Configure.py (renamed from Configure.py)0
-rw-r--r--waflib/Context.py (renamed from Context.py)0
-rw-r--r--waflib/Errors.py (renamed from Errors.py)0
-rw-r--r--waflib/Logs.py (renamed from Logs.py)0
-rw-r--r--waflib/Node.py (renamed from Node.py)0
-rw-r--r--waflib/Options.py (renamed from Options.py)0
-rw-r--r--waflib/README.md (renamed from README.md)0
-rw-r--r--waflib/Runner.py (renamed from Runner.py)0
-rw-r--r--waflib/Scripting.py (renamed from Scripting.py)0
-rw-r--r--waflib/Task.py (renamed from Task.py)0
-rw-r--r--waflib/TaskGen.py (renamed from TaskGen.py)0
-rw-r--r--waflib/Tools/__init__.py (renamed from Tools/__init__.py)0
-rw-r--r--waflib/Tools/ar.py (renamed from Tools/ar.py)0
-rw-r--r--waflib/Tools/asm.py (renamed from Tools/asm.py)0
-rw-r--r--waflib/Tools/bison.py (renamed from Tools/bison.py)0
-rw-r--r--waflib/Tools/c.py (renamed from Tools/c.py)0
-rw-r--r--waflib/Tools/c_aliases.py (renamed from Tools/c_aliases.py)0
-rw-r--r--waflib/Tools/c_config.py (renamed from Tools/c_config.py)0
-rw-r--r--waflib/Tools/c_osx.py (renamed from Tools/c_osx.py)0
-rw-r--r--waflib/Tools/c_preproc.py (renamed from Tools/c_preproc.py)0
-rw-r--r--waflib/Tools/c_tests.py (renamed from Tools/c_tests.py)0
-rw-r--r--waflib/Tools/ccroot.py (renamed from Tools/ccroot.py)0
-rw-r--r--waflib/Tools/clang.py (renamed from Tools/clang.py)0
-rw-r--r--waflib/Tools/clangxx.py (renamed from Tools/clangxx.py)0
-rw-r--r--waflib/Tools/compiler_c.py (renamed from Tools/compiler_c.py)0
-rw-r--r--waflib/Tools/compiler_cxx.py (renamed from Tools/compiler_cxx.py)0
-rw-r--r--waflib/Tools/compiler_d.py (renamed from Tools/compiler_d.py)0
-rw-r--r--waflib/Tools/compiler_fc.py (renamed from Tools/compiler_fc.py)0
-rw-r--r--waflib/Tools/cs.py (renamed from Tools/cs.py)0
-rw-r--r--waflib/Tools/cxx.py (renamed from Tools/cxx.py)0
-rw-r--r--waflib/Tools/d.py (renamed from Tools/d.py)0
-rw-r--r--waflib/Tools/d_config.py (renamed from Tools/d_config.py)0
-rw-r--r--waflib/Tools/d_scan.py (renamed from Tools/d_scan.py)0
-rw-r--r--waflib/Tools/dbus.py (renamed from Tools/dbus.py)0
-rw-r--r--waflib/Tools/dmd.py (renamed from Tools/dmd.py)0
-rw-r--r--waflib/Tools/errcheck.py (renamed from Tools/errcheck.py)0
-rw-r--r--waflib/Tools/fc.py (renamed from Tools/fc.py)0
-rw-r--r--waflib/Tools/fc_config.py (renamed from Tools/fc_config.py)0
-rw-r--r--waflib/Tools/fc_scan.py (renamed from Tools/fc_scan.py)0
-rw-r--r--waflib/Tools/flex.py (renamed from Tools/flex.py)0
-rw-r--r--waflib/Tools/g95.py (renamed from Tools/g95.py)0
-rw-r--r--waflib/Tools/gas.py (renamed from Tools/gas.py)0
-rw-r--r--waflib/Tools/gcc.py (renamed from Tools/gcc.py)0
-rw-r--r--waflib/Tools/gdc.py (renamed from Tools/gdc.py)0
-rw-r--r--waflib/Tools/gfortran.py (renamed from Tools/gfortran.py)0
-rw-r--r--waflib/Tools/glib2.py (renamed from Tools/glib2.py)0
-rw-r--r--waflib/Tools/gnu_dirs.py (renamed from Tools/gnu_dirs.py)0
-rw-r--r--waflib/Tools/gxx.py (renamed from Tools/gxx.py)0
-rw-r--r--waflib/Tools/icc.py (renamed from Tools/icc.py)0
-rw-r--r--waflib/Tools/icpc.py (renamed from Tools/icpc.py)0
-rw-r--r--waflib/Tools/ifort.py (renamed from Tools/ifort.py)0
-rw-r--r--waflib/Tools/intltool.py (renamed from Tools/intltool.py)0
-rw-r--r--waflib/Tools/irixcc.py (renamed from Tools/irixcc.py)0
-rw-r--r--waflib/Tools/javaw.py (renamed from Tools/javaw.py)0
-rw-r--r--waflib/Tools/ldc2.py (renamed from Tools/ldc2.py)0
-rw-r--r--waflib/Tools/lua.py (renamed from Tools/lua.py)0
-rw-r--r--waflib/Tools/md5_tstamp.py (renamed from Tools/md5_tstamp.py)0
-rw-r--r--waflib/Tools/msvc.py (renamed from Tools/msvc.py)0
-rw-r--r--waflib/Tools/nasm.py (renamed from Tools/nasm.py)0
-rw-r--r--waflib/Tools/nobuild.py (renamed from Tools/nobuild.py)0
-rw-r--r--waflib/Tools/perl.py (renamed from Tools/perl.py)0
-rw-r--r--waflib/Tools/python.py (renamed from Tools/python.py)0
-rw-r--r--waflib/Tools/qt5.py (renamed from Tools/qt5.py)0
-rw-r--r--waflib/Tools/ruby.py (renamed from Tools/ruby.py)0
-rw-r--r--waflib/Tools/suncc.py (renamed from Tools/suncc.py)0
-rw-r--r--waflib/Tools/suncxx.py (renamed from Tools/suncxx.py)0
-rw-r--r--waflib/Tools/tex.py (renamed from Tools/tex.py)0
-rw-r--r--waflib/Tools/vala.py (renamed from Tools/vala.py)0
-rw-r--r--waflib/Tools/waf_unit_test.py (renamed from Tools/waf_unit_test.py)0
-rw-r--r--waflib/Tools/winres.py (renamed from Tools/winres.py)0
-rw-r--r--waflib/Tools/xlc.py (renamed from Tools/xlc.py)0
-rw-r--r--waflib/Tools/xlcxx.py (renamed from Tools/xlcxx.py)0
-rw-r--r--waflib/Utils.py (renamed from Utils.py)0
-rw-r--r--waflib/__init__.py (renamed from __init__.py)0
-rw-r--r--waflib/ansiterm.py (renamed from ansiterm.py)0
-rw-r--r--waflib/extras/__init__.py (renamed from extras/__init__.py)0
-rw-r--r--waflib/extras/autowaf.py (renamed from extras/autowaf.py)0
-rw-r--r--waflib/extras/batched_cc.py (renamed from extras/batched_cc.py)0
-rw-r--r--waflib/extras/biber.py (renamed from extras/biber.py)0
-rw-r--r--waflib/extras/bjam.py (renamed from extras/bjam.py)0
-rw-r--r--waflib/extras/blender.py (renamed from extras/blender.py)0
-rw-r--r--waflib/extras/boo.py (renamed from extras/boo.py)0
-rw-r--r--waflib/extras/boost.py (renamed from extras/boost.py)0
-rw-r--r--waflib/extras/build_file_tracker.py (renamed from extras/build_file_tracker.py)0
-rw-r--r--waflib/extras/build_logs.py (renamed from extras/build_logs.py)0
-rw-r--r--waflib/extras/buildcopy.py (renamed from extras/buildcopy.py)0
-rw-r--r--waflib/extras/c_bgxlc.py (renamed from extras/c_bgxlc.py)0
-rw-r--r--waflib/extras/c_dumbpreproc.py (renamed from extras/c_dumbpreproc.py)0
-rw-r--r--waflib/extras/c_emscripten.py (renamed from extras/c_emscripten.py)0
-rw-r--r--waflib/extras/c_nec.py (renamed from extras/c_nec.py)0
-rw-r--r--waflib/extras/cabal.py (renamed from extras/cabal.py)0
-rw-r--r--waflib/extras/cfg_altoptions.py (renamed from extras/cfg_altoptions.py)0
-rw-r--r--waflib/extras/clang_compilation_database.py (renamed from extras/clang_compilation_database.py)0
-rw-r--r--waflib/extras/codelite.py (renamed from extras/codelite.py)0
-rw-r--r--waflib/extras/color_gcc.py (renamed from extras/color_gcc.py)0
-rw-r--r--waflib/extras/color_rvct.py (renamed from extras/color_rvct.py)0
-rw-r--r--waflib/extras/compat15.py (renamed from extras/compat15.py)0
-rw-r--r--waflib/extras/cppcheck.py (renamed from extras/cppcheck.py)0
-rw-r--r--waflib/extras/cpplint.py (renamed from extras/cpplint.py)0
-rw-r--r--waflib/extras/cross_gnu.py (renamed from extras/cross_gnu.py)0
-rw-r--r--waflib/extras/cython.py (renamed from extras/cython.py)0
-rw-r--r--waflib/extras/dcc.py (renamed from extras/dcc.py)0
-rw-r--r--waflib/extras/distnet.py (renamed from extras/distnet.py)0
-rw-r--r--waflib/extras/doxygen.py (renamed from extras/doxygen.py)0
-rw-r--r--waflib/extras/dpapi.py (renamed from extras/dpapi.py)0
-rw-r--r--waflib/extras/eclipse.py (renamed from extras/eclipse.py)0
-rw-r--r--waflib/extras/erlang.py (renamed from extras/erlang.py)0
-rw-r--r--waflib/extras/fast_partial.py (renamed from extras/fast_partial.py)0
-rw-r--r--waflib/extras/fc_bgxlf.py (renamed from extras/fc_bgxlf.py)0
-rw-r--r--waflib/extras/fc_cray.py (renamed from extras/fc_cray.py)0
-rw-r--r--waflib/extras/fc_nag.py (renamed from extras/fc_nag.py)0
-rw-r--r--waflib/extras/fc_nec.py (renamed from extras/fc_nec.py)0
-rw-r--r--waflib/extras/fc_open64.py (renamed from extras/fc_open64.py)0
-rw-r--r--waflib/extras/fc_pgfortran.py (renamed from extras/fc_pgfortran.py)0
-rw-r--r--waflib/extras/fc_solstudio.py (renamed from extras/fc_solstudio.py)0
-rw-r--r--waflib/extras/fc_xlf.py (renamed from extras/fc_xlf.py)0
-rw-r--r--waflib/extras/file_to_object.py (renamed from extras/file_to_object.py)0
-rw-r--r--waflib/extras/fluid.py (renamed from extras/fluid.py)0
-rw-r--r--waflib/extras/freeimage.py (renamed from extras/freeimage.py)0
-rw-r--r--waflib/extras/fsb.py (renamed from extras/fsb.py)0
-rw-r--r--waflib/extras/fsc.py (renamed from extras/fsc.py)0
-rw-r--r--waflib/extras/gccdeps.py (renamed from extras/gccdeps.py)0
-rw-r--r--waflib/extras/gdbus.py (renamed from extras/gdbus.py)0
-rw-r--r--waflib/extras/gob2.py (renamed from extras/gob2.py)0
-rw-r--r--waflib/extras/halide.py (renamed from extras/halide.py)0
-rwxr-xr-xwaflib/extras/javatest.py (renamed from extras/javatest.py)0
-rw-r--r--waflib/extras/kde4.py (renamed from extras/kde4.py)0
-rw-r--r--waflib/extras/local_rpath.py (renamed from extras/local_rpath.py)0
-rw-r--r--waflib/extras/lv2.py (renamed from extras/lv2.py)0
-rw-r--r--waflib/extras/make.py (renamed from extras/make.py)0
-rw-r--r--waflib/extras/midl.py (renamed from extras/midl.py)0
-rw-r--r--waflib/extras/msvcdeps.py (renamed from extras/msvcdeps.py)0
-rw-r--r--waflib/extras/msvs.py (renamed from extras/msvs.py)0
-rw-r--r--waflib/extras/netcache_client.py (renamed from extras/netcache_client.py)0
-rw-r--r--waflib/extras/objcopy.py (renamed from extras/objcopy.py)0
-rw-r--r--waflib/extras/ocaml.py (renamed from extras/ocaml.py)0
-rw-r--r--waflib/extras/package.py (renamed from extras/package.py)0
-rw-r--r--waflib/extras/parallel_debug.py (renamed from extras/parallel_debug.py)0
-rw-r--r--waflib/extras/pch.py (renamed from extras/pch.py)0
-rw-r--r--waflib/extras/pep8.py (renamed from extras/pep8.py)0
-rw-r--r--waflib/extras/pgicc.py (renamed from extras/pgicc.py)0
-rw-r--r--waflib/extras/pgicxx.py (renamed from extras/pgicxx.py)0
-rw-r--r--waflib/extras/proc.py (renamed from extras/proc.py)0
-rw-r--r--waflib/extras/protoc.py (renamed from extras/protoc.py)0
-rw-r--r--waflib/extras/pyqt5.py (renamed from extras/pyqt5.py)0
-rw-r--r--waflib/extras/pytest.py (renamed from extras/pytest.py)0
-rw-r--r--waflib/extras/qnxnto.py (renamed from extras/qnxnto.py)0
-rw-r--r--waflib/extras/qt4.py (renamed from extras/qt4.py)0
-rw-r--r--waflib/extras/relocation.py (renamed from extras/relocation.py)0
-rw-r--r--waflib/extras/remote.py (renamed from extras/remote.py)0
-rw-r--r--waflib/extras/resx.py (renamed from extras/resx.py)0
-rw-r--r--waflib/extras/review.py (renamed from extras/review.py)0
-rw-r--r--waflib/extras/rst.py (renamed from extras/rst.py)0
-rw-r--r--waflib/extras/run_do_script.py (renamed from extras/run_do_script.py)0
-rw-r--r--waflib/extras/run_m_script.py (renamed from extras/run_m_script.py)0
-rw-r--r--waflib/extras/run_py_script.py (renamed from extras/run_py_script.py)0
-rw-r--r--waflib/extras/run_r_script.py (renamed from extras/run_r_script.py)0
-rw-r--r--waflib/extras/sas.py (renamed from extras/sas.py)0
-rw-r--r--waflib/extras/satellite_assembly.py (renamed from extras/satellite_assembly.py)0
-rw-r--r--waflib/extras/scala.py (renamed from extras/scala.py)0
-rw-r--r--waflib/extras/slow_qt4.py (renamed from extras/slow_qt4.py)0
-rw-r--r--waflib/extras/softlink_libs.py (renamed from extras/softlink_libs.py)0
-rw-r--r--waflib/extras/stale.py (renamed from extras/stale.py)0
-rw-r--r--waflib/extras/stracedeps.py (renamed from extras/stracedeps.py)0
-rw-r--r--waflib/extras/swig.py (renamed from extras/swig.py)0
-rw-r--r--waflib/extras/syms.py (renamed from extras/syms.py)0
-rw-r--r--waflib/extras/ticgt.py (renamed from extras/ticgt.py)0
-rw-r--r--waflib/extras/unity.py (renamed from extras/unity.py)0
-rw-r--r--waflib/extras/use_config.py (renamed from extras/use_config.py)0
-rw-r--r--waflib/extras/valadoc.py (renamed from extras/valadoc.py)0
-rw-r--r--waflib/extras/waf_xattr.py (renamed from extras/waf_xattr.py)0
-rw-r--r--waflib/extras/why.py (renamed from extras/why.py)0
-rw-r--r--waflib/extras/win32_opts.py (renamed from extras/win32_opts.py)0
-rw-r--r--waflib/extras/wix.py (renamed from extras/wix.py)0
-rw-r--r--waflib/extras/xcode6.py (renamed from extras/xcode6.py)0
-rw-r--r--waflib/fixpy2.py (renamed from fixpy2.py)0
-rwxr-xr-xwaflib/processor.py (renamed from processor.py)0
-rwxr-xr-xwaflib/waf16
-rw-r--r--wscript506
284 files changed, 24533 insertions, 34 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..f063da3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+waf binary \ No newline at end of file
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3cea4ab
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,11 @@
+Author:
+ David Robillard <d@drobilla.net>
+
+GTK2 GUI and I18N support:
+ Lars Luthman <lars.luthman@gmail.com>
+
+Dynamic manifest support:
+ Stefano D'Angelo
+
+Plugin execution via Python bindings:
+ Kaspar Emanuel <kaspar.emanuel@gmail.com> \ No newline at end of file
diff --git a/COPYING b/COPYING
index a4147d2..908a4e0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,25 +1,13 @@
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-3. The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
-IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+Copyright 2011-2017 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. \ No newline at end of file
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..9b54f51
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,66 @@
+Installation Instructions
+=========================
+
+Basic Installation
+------------------
+
+Building this software requires only Python. To install with default options:
+
+ ./waf configure
+ ./waf
+ ./waf install # or 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
+
+Library Versioning
+------------------
+
+This library uses semantic versioning <http://semver.org/>.
+
+Several major versions can be installed in parallel. The shared library name,
+include directory, and pkg-config file are suffixed with the major version
+number. For example, a library named "foo" at version 1.x.y might install:
+
+ /usr/include/foo-1/foo/foo.h
+ /usr/lib/foo-1.so.1.x.y
+ /usr/lib/pkgconfig/foo-1.pc
+
+Dependencies can check for the package "foo-1" with pkg-config.
+
+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
+
+Packages should allow parallel installation of several major versions. For
+example, the above would be packaged as "foo-1". \ No newline at end of file
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..5f02a5c
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,228 @@
+lilv (0.24.5) unstable;
+
+ * Fix GCC8 warnings
+
+ -- David Robillard <d@drobilla.net> Tue, 04 Sep 2018 21:02:19 +0200
+
+lilv (0.24.4) stable;
+
+ * Fix saving state when broken links are encountered
+ * Don't attempt to load remote or non-Turtle files
+ * lv2apply: Activate plugin before running
+ * lv2apply: Use default values when they are not nan
+ * lv2bench: Improve support for plugins with sequence ports
+ * lv2bench: Support running a single plugin given on the command line
+ * Gracefully handle plugins with missing binary URIs
+ * Remove use of deprecated readdir_r
+ * Install Python bindings when configured without tests
+ (thanks Clement Skau)
+
+ -- David Robillard <d@drobilla.net> Sun, 22 Jul 2018 20:42:00 +0200
+
+lilv (0.24.2) stable;
+
+ * Fix saving state to paths that contain URI delimiters (#, ?, etc)
+ * Fix comparison of restored states with paths
+
+ -- David Robillard <d@drobilla.net> Wed, 04 Jan 2017 11:48:08 -0500
+
+lilv (0.24.0) stable;
+
+ * Add new hand-crafted Pythonic bindings with full test coverage
+ * Add lv2apply utility for applying plugins to audio files
+ * Add lilv_world_get_symbol()
+ * Add lilv_state_set_metadata() for adding state banks/comments/etc
+ (based on patch from Hanspeter Portner)
+ * Fix crash when state contains non-POD properties
+ * Fix crash when NULL predicate is passed to lilv_world_find_nodes()
+ * Fix state file versioning
+ * Unload contained resources when bundle is unloaded
+ * Do not instantiate plugin when data fails to parse
+ * Support re-loading plugins
+ * Replace bundles if bundle with newer plugin version is loaded
+ (based on patch from Robin Gareus)
+ * Fix loading dyn-manifest from bundles with spaces in their path
+ * Check lv2:binary predicate for UIs
+ * Add LILV_URI_ATOM_PORT and LILV_URI_CV_PORT defines
+ * Fix documentation installation
+ * Fix outdated comment references to lilv_uri_to_path()
+
+ -- David Robillard <d@drobilla.net> Mon, 19 Sep 2016 22:24:57 -0400
+
+lilv (0.22.0) stable;
+
+ * Fix loading files with spaces in their path
+ * Add lilv_file_uri_parse() for correct URI to path conversion
+ * Add lilv_node_get_path() for convenient file URI path access
+ * Add lilv_state_emit_port_values() for special port value handling
+ * Add lilv_state_get_uri()
+ * Add lilv_state_delete() for deleting user saved presets
+ * Add lilv_free() for systems picky about such things
+ * Fix lilv_world_ask() to work with wildcards
+ * Fix creation of duplicate manifest entries when saving state
+ * Fix bindings for Python 3
+ * Load discovered owl ontologies as specifications
+ * Expose lilv_world_load_specifications() and
+ lilv_world_load_plugin_classes()
+ * Tolerate passing NULL to lilv_state_restore()
+ * Preserve absolute paths in state if no link directory is given
+ * Fix a few minor/unlikely memory errors
+ * Configure based on compiler target OS for cross-compilation
+ * Fix lilv_realpath() on pre-POSIX-2008 systems
+ * Fix directory walking on some systems (thanks Matt Fischer)
+ * Windows fixes (thanks John Emmas)
+ * Minor documentation improvements
+ * Upgrade to waf 1.8.14
+
+ -- David Robillard <d@drobilla.net> Thu, 08 Oct 2015 15:39:29 -0400
+
+lilv (0.20.0) stable;
+
+ * Don't load files multiple times if they are listed as rdfs:seeAlso for
+ several plugins
+ * Call lv2_lib_descriptor separately for different bundle paths
+ (fix loading several dynamic plugins like Ingen at once)
+ * Tolerate calling lilv_node_as_uri or lilv_node_as_blank on NULL
+ * Add convenient lilv_new_file_uri for creating file URIs
+ * Fix use of lv2info -m and -p options to write plugin data
+ (useful for porting plugins bridges with NASPRO)
+ * Fix issues with lilv_plugin_get_author_name and friends
+ (thanks Filipe Coelho)
+ * Improved/working lv2_apply.py to apply plugin to a .wav
+ (thanks Joe Button)
+ * Add lilv_world_unload_bundle() and lilv_world_unload_resource()
+ * Fix several minor memory leaks
+ * Improve test coverage
+ * Upgrade to waf 1.7.16
+
+ -- David Robillard <d@drobilla.net> Fri, 08 Aug 2014 18:21:32 -0400
+
+lilv (0.18.0) stable;
+
+ * Allow lilv_state_restore() to be used without passing an instance,
+ for restoring port values via a callback only
+ * Fix unlikely memory leak in lilv_plugin_instantiate()
+ * Support denoting latency ports with lv2:designation lv2:latency
+ * Allow passing NULL port_class to lilv_plugin_get_port_by_designation
+ * Call GetProcAddress with correct calling convention on Windows
+ * Add support for running plugins from Python by Kaspar Emanuel
+ * Clean up after test suite so multiple runs are successful
+ * Add lilv_port_get_node() for using world query functions with ports
+ * lv2info: Don't display invalid control maxes and defaults
+ (patch from Robin Gareus)
+ * lilvmm.hpp: Add wrappers for UI API
+
+ -- David Robillard <d@drobilla.net> Sat, 04 Jan 2014 16:06:42 -0500
+
+lilv (0.16.0) stable;
+
+ * Add lilv_world_ask() for easily checking if a statement exists
+ * Add lilv_world_get() and lilv_port_get() for easily getting one value
+ * Add lilv_nodes_merge()
+ * Make lilv_plugin_get_port_by_designation() return a const pointer
+ * Require a URI for lilv_state_to_string() and fail gracefully otherwise
+ * Fail gracefully when lilv_state_new_from_string() is called on NULL
+ * Make state loading functions fall back to lv2:default for port values,
+ so a plugin description can be loaded as default state
+ * Ignore state ports with no value instead of printing an error
+ * Support atom:supports in lilv_port_supports_event()
+ * Add va_list variant of lilv_plugin_get_num_ports_of_class()
+ * Fix several plugin functions that failed to load data if called first
+ * Correctly depend on serd at build time (fix compilation in odd cases)
+ * Disable timestamps in HTML documentation for reproducible build
+ * lilvmm.hpp: Support varargs for Plugin::get_num_ports_of_class()
+ * lilvmm.hpp: Add several missing methods
+ * Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory)
+
+ -- David Robillard <d@drobilla.net> Mon, 18 Feb 2013 16:43:10 -0500
+
+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
diff --git a/README b/README
new file mode 100644
index 0000000..807fc2b
--- /dev/null
+++ b/README
@@ -0,0 +1,8 @@
+Lilv
+====
+
+Lilv is a C library to make the use of LV2 plugins as simple as possible for
+applications.
+For more information, see <http://drobilla.net/software/lilv>.
+
+ -- David Robillard <d@drobilla.net>
diff --git a/bindings/lilv.i b/bindings/lilv.i
new file mode 100644
index 0000000..f6254a7
--- /dev/null
+++ b/bindings/lilv.i
@@ -0,0 +1,66 @@
+%module lilv
+%typedef unsigned uint32_t;
+%{
+#define SWIG_FILE_WITH_INIT
+#include "lilv/lilv.h"
+#include "lilv/lilvmm.hpp"
+%}
+
+%include "numpy.i"
+%init %{
+ import_array();
+%}
+%apply (float* INPLACE_ARRAY1) {(void* data_location)}
+
+%feature("compactdefaultargs") %{
+ lilv_plugin_get_num_ports_of_class;
+ get_num_ports_of_class;
+%}
+%varargs(3, LilvNode* node = NULL) lilv_plugin_get_num_ports_of_class;
+%varargs(3, LilvNode* node = NULL) get_num_ports_of_class;
+%typemap(in, numinputs=0) LilvNode *node3 ""; // Make sure it's NULL terminated
+
+%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):
+ if self.plugins.is_end(self.iter):
+ raise StopIteration
+ plugin = self.plugins.get(self.iter)
+ self.iter = self.plugins.next(self.iter)
+ return plugin
+
+ def next(self):
+ "Python 2 iterator protocol"
+ return Iterator.__next__(self)
+
+ return Iterator(self)
+
+ def get_by_uri(self, *args):
+ """get_by_uri(self, LilvNode uri) -> PluginClass"""
+ ret = _lilv.Plugins_get_by_uri(self, *args)
+ if ret.me is None:
+ return None
+ else:
+ return ret
+%}
+};
+
+%extend Node {
+%pythoncode %{
+ def __str__(self):
+ return self.get_turtle_token()
+%}
+};
+
+} /* namespace Lilv */
diff --git a/bindings/numpy.i b/bindings/numpy.i
new file mode 100644
index 0000000..d695b36
--- /dev/null
+++ b/bindings/numpy.i
@@ -0,0 +1,1746 @@
+/* -*- C -*- (not really, but good for syntax highlighting) */
+#ifdef SWIGPYTHON
+
+%{
+#ifndef SWIG_FILE_WITH_INIT
+# define NO_IMPORT_ARRAY
+#endif
+#include "stdio.h"
+#include <numpy/arrayobject.h>
+%}
+
+/**********************************************************************/
+
+%fragment("NumPy_Backward_Compatibility", "header")
+{
+/* Support older NumPy data type names
+*/
+%#if NDARRAY_VERSION < 0x01000000
+%#define NPY_BOOL PyArray_BOOL
+%#define NPY_BYTE PyArray_BYTE
+%#define NPY_UBYTE PyArray_UBYTE
+%#define NPY_SHORT PyArray_SHORT
+%#define NPY_USHORT PyArray_USHORT
+%#define NPY_INT PyArray_INT
+%#define NPY_UINT PyArray_UINT
+%#define NPY_LONG PyArray_LONG
+%#define NPY_ULONG PyArray_ULONG
+%#define NPY_LONGLONG PyArray_LONGLONG
+%#define NPY_ULONGLONG PyArray_ULONGLONG
+%#define NPY_FLOAT PyArray_FLOAT
+%#define NPY_DOUBLE PyArray_DOUBLE
+%#define NPY_LONGDOUBLE PyArray_LONGDOUBLE
+%#define NPY_CFLOAT PyArray_CFLOAT
+%#define NPY_CDOUBLE PyArray_CDOUBLE
+%#define NPY_CLONGDOUBLE PyArray_CLONGDOUBLE
+%#define NPY_OBJECT PyArray_OBJECT
+%#define NPY_STRING PyArray_STRING
+%#define NPY_UNICODE PyArray_UNICODE
+%#define NPY_VOID PyArray_VOID
+%#define NPY_NTYPES PyArray_NTYPES
+%#define NPY_NOTYPE PyArray_NOTYPE
+%#define NPY_CHAR PyArray_CHAR
+%#define NPY_USERDEF PyArray_USERDEF
+%#define npy_intp intp
+
+%#define NPY_MAX_BYTE MAX_BYTE
+%#define NPY_MIN_BYTE MIN_BYTE
+%#define NPY_MAX_UBYTE MAX_UBYTE
+%#define NPY_MAX_SHORT MAX_SHORT
+%#define NPY_MIN_SHORT MIN_SHORT
+%#define NPY_MAX_USHORT MAX_USHORT
+%#define NPY_MAX_INT MAX_INT
+%#define NPY_MIN_INT MIN_INT
+%#define NPY_MAX_UINT MAX_UINT
+%#define NPY_MAX_LONG MAX_LONG
+%#define NPY_MIN_LONG MIN_LONG
+%#define NPY_MAX_ULONG MAX_ULONG
+%#define NPY_MAX_LONGLONG MAX_LONGLONG
+%#define NPY_MIN_LONGLONG MIN_LONGLONG
+%#define NPY_MAX_ULONGLONG MAX_ULONGLONG
+%#define NPY_MAX_INTP MAX_INTP
+%#define NPY_MIN_INTP MIN_INTP
+
+%#define NPY_FARRAY FARRAY
+%#define NPY_F_CONTIGUOUS F_CONTIGUOUS
+%#endif
+}
+
+/**********************************************************************/
+
+/* The following code originally appeared in
+ * enthought/kiva/agg/src/numeric.i written by Eric Jones. It was
+ * translated from C++ to C by John Hunter. Bill Spotz has modified
+ * it to fix some minor bugs, upgrade from Numeric to numpy (all
+ * versions), add some comments and functionality, and convert from
+ * direct code insertion to SWIG fragments.
+ */
+
+%fragment("NumPy_Macros", "header")
+{
+/* Macros to extract array attributes.
+ */
+%#define is_array(a) ((a) && PyArray_Check((PyArrayObject *)a))
+%#define array_type(a) (int)(PyArray_TYPE(a))
+%#define array_numdims(a) (((PyArrayObject *)a)->nd)
+%#define array_dimensions(a) (((PyArrayObject *)a)->dimensions)
+%#define array_size(a,i) (((PyArrayObject *)a)->dimensions[i])
+%#define array_data(a) (((PyArrayObject *)a)->data)
+%#define array_is_contiguous(a) (PyArray_ISCONTIGUOUS(a))
+%#define array_is_native(a) (PyArray_ISNOTSWAPPED(a))
+%#define array_is_fortran(a) (PyArray_ISFORTRAN(a))
+}
+
+/**********************************************************************/
+
+%fragment("NumPy_Utilities", "header")
+{
+ /* Given a PyObject, return a string describing its type.
+ */
+ const char* pytype_string(PyObject* py_obj) {
+ if (py_obj == NULL ) return "C NULL value";
+ if (py_obj == Py_None ) return "Python None" ;
+ if (PyCallable_Check(py_obj)) return "callable" ;
+ if (PyString_Check( py_obj)) return "string" ;
+ if (PyInt_Check( py_obj)) return "int" ;
+ if (PyFloat_Check( py_obj)) return "float" ;
+ if (PyDict_Check( py_obj)) return "dict" ;
+ if (PyList_Check( py_obj)) return "list" ;
+ if (PyTuple_Check( py_obj)) return "tuple" ;
+ if (PyModule_Check( py_obj)) return "module" ;
+%#if PY_MAJOR_VERSION < 3
+ if (PyFile_Check( py_obj)) return "file" ;
+ if (PyInstance_Check(py_obj)) return "instance" ;
+%#endif
+
+ return "unkown type";
+ }
+
+ /* Given a NumPy typecode, return a string describing the type.
+ */
+ const char* typecode_string(int typecode) {
+ static const char* type_names[25] = {"bool", "byte", "unsigned byte",
+ "short", "unsigned short", "int",
+ "unsigned int", "long", "unsigned long",
+ "long long", "unsigned long long",
+ "float", "double", "long double",
+ "complex float", "complex double",
+ "complex long double", "object",
+ "string", "unicode", "void", "ntypes",
+ "notype", "char", "unknown"};
+ return typecode < 24 ? type_names[typecode] : type_names[24];
+ }
+
+ /* Make sure input has correct numpy type. Allow character and byte
+ * to match. Also allow int and long to match. This is deprecated.
+ * You should use PyArray_EquivTypenums() instead.
+ */
+ int type_match(int actual_type, int desired_type) {
+ return PyArray_EquivTypenums(actual_type, desired_type);
+ }
+}
+
+/**********************************************************************/
+
+%fragment("NumPy_Object_to_Array", "header",
+ fragment="NumPy_Backward_Compatibility",
+ fragment="NumPy_Macros",
+ fragment="NumPy_Utilities")
+{
+ /* Given a PyObject pointer, cast it to a PyArrayObject pointer if
+ * legal. If not, set the python error string appropriately and
+ * return NULL.
+ */
+ PyArrayObject* obj_to_array_no_conversion(PyObject* input, int typecode)
+ {
+ PyArrayObject* ary = NULL;
+ if (is_array(input) && (typecode == NPY_NOTYPE ||
+ PyArray_EquivTypenums(array_type(input), typecode)))
+ {
+ ary = (PyArrayObject*) input;
+ }
+ else if is_array(input)
+ {
+ const char* desired_type = typecode_string(typecode);
+ const char* actual_type = typecode_string(array_type(input));
+ PyErr_Format(PyExc_TypeError,
+ "Array of type '%s' required. Array of type '%s' given",
+ desired_type, actual_type);
+ ary = NULL;
+ }
+ else
+ {
+ const char * desired_type = typecode_string(typecode);
+ const char * actual_type = pytype_string(input);
+ PyErr_Format(PyExc_TypeError,
+ "Array of type '%s' required. A '%s' was given",
+ desired_type, actual_type);
+ ary = NULL;
+ }
+ return ary;
+ }
+
+ /* Convert the given PyObject to a NumPy array with the given
+ * typecode. On success, return a valid PyArrayObject* with the
+ * correct type. On failure, the python error string will be set and
+ * the routine returns NULL.
+ */
+ PyArrayObject* obj_to_array_allow_conversion(PyObject* input, int typecode,
+ int* is_new_object)
+ {
+ PyArrayObject* ary = NULL;
+ PyObject* py_obj;
+ if (is_array(input) && (typecode == NPY_NOTYPE ||
+ PyArray_EquivTypenums(array_type(input),typecode)))
+ {
+ ary = (PyArrayObject*) input;
+ *is_new_object = 0;
+ }
+ else
+ {
+ py_obj = PyArray_FROMANY(input, typecode, 0, 0, NPY_DEFAULT);
+ /* If NULL, PyArray_FromObject will have set python error value.*/
+ ary = (PyArrayObject*) py_obj;
+ *is_new_object = 1;
+ }
+ return ary;
+ }
+
+ /* Given a PyArrayObject, check to see if it is contiguous. If so,
+ * return the input pointer and flag it as not a new object. If it is
+ * not contiguous, create a new PyArrayObject using the original data,
+ * flag it as a new object and return the pointer.
+ */
+ PyArrayObject* make_contiguous(PyArrayObject* ary, int* is_new_object,
+ int min_dims, int max_dims)
+ {
+ PyArrayObject* result;
+ if (array_is_contiguous(ary))
+ {
+ result = ary;
+ *is_new_object = 0;
+ }
+ else
+ {
+ result = (PyArrayObject*) PyArray_ContiguousFromObject((PyObject*)ary,
+ array_type(ary),
+ min_dims,
+ max_dims);
+ *is_new_object = 1;
+ }
+ return result;
+ }
+
+ /* Given a PyArrayObject, check to see if it is Fortran-contiguous.
+ * If so, return the input pointer, but do not flag it as not a new
+ * object. If it is not Fortran-contiguous, create a new
+ * PyArrayObject using the original data, flag it as a new object
+ * and return the pointer.
+ */
+ PyArrayObject* make_fortran(PyArrayObject* ary, int* is_new_object,
+ int min_dims, int max_dims)
+ {
+ PyArrayObject* result;
+ if (array_is_fortran(ary))
+ {
+ result = ary;
+ *is_new_object = 0;
+ }
+ else
+ {
+ Py_INCREF(ary->descr);
+ result = (PyArrayObject*) PyArray_FromArray(ary, ary->descr, NPY_FORTRAN);
+ *is_new_object = 1;
+ }
+ return result;
+ }
+
+ /* Convert a given PyObject to a contiguous PyArrayObject of the
+ * specified type. If the input object is not a contiguous
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_allow_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_contiguous(ary1, &is_new2, 0, 0);
+ if ( is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+
+ /* Convert a given PyObject to a Fortran-ordered PyArrayObject of the
+ * specified type. If the input object is not a Fortran-ordered
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_fortran_allow_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_allow_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_fortran(ary1, &is_new2, 0, 0);
+ if (is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+
+ /* The following code was added by Ilmar M. Wilbers for forcing a copy of the
+ * object even when it is a NumPy array. This is meant for use with the
+ * IN_ARRAY typemaps, and allows the user to perform changes on an array
+ * without these chenges being reflected in the calling code.
+ */
+
+ /* Convert the given PyObject to a NumPy array with the given
+ * typecode as a copy. On success, return a valid PyArrayObject* with the
+ * correct type. On failure, the python error string will be set and
+ * the routine returns NULL.
+ */
+ PyArrayObject* obj_to_array_force_conversion(PyObject* input, int typecode,
+ int* is_new_object)
+ {
+ PyArrayObject* ary = NULL;
+ PyObject* py_obj;
+ if (is_array(input) && (typecode == NPY_NOTYPE ||
+ PyArray_EquivTypenums(array_type(input),typecode)))
+ {
+ py_obj = PyArray_Copy((PyArrayObject*) input);
+ ary = (PyArrayObject*) py_obj;
+ *is_new_object = 1;
+ }
+ else
+ {
+ py_obj = PyArray_FROMANY(input, typecode, 0, 0, NPY_DEFAULT);
+ /* If NULL, PyArray_FromObject will have set python error value.*/
+ ary = (PyArrayObject*) py_obj;
+ *is_new_object = 1;
+ }
+ return ary;
+ }
+
+ /* Convert a given PyObject to a contiguous PyArrayObject of the
+ * specified type. If the input object is not a contiguous
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_contiguous_force_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_force_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_contiguous(ary1, &is_new2, 0, 0);
+ if ( is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+
+ /* Convert a given PyObject to a Fortran-ordered PyArrayObject of the
+ * specified type. If the input object is not a Fortran-ordered
+ * PyArrayObject, a new one will be created and the new object flag
+ * will be set.
+ */
+ PyArrayObject* obj_to_array_fortran_force_conversion(PyObject* input,
+ int typecode,
+ int* is_new_object)
+ {
+ int is_new1 = 0;
+ int is_new2 = 0;
+ PyArrayObject* ary2;
+ PyArrayObject* ary1 = obj_to_array_force_conversion(input, typecode,
+ &is_new1);
+ if (ary1)
+ {
+ ary2 = make_fortran(ary1, &is_new2, 0, 0);
+ if (is_new1 && is_new2)
+ {
+ Py_DECREF(ary1);
+ }
+ ary1 = ary2;
+ }
+ *is_new_object = is_new1 || is_new2;
+ return ary1;
+ }
+ /* End modifications by Ilmar M. Wilbers
+ */
+
+} /* end fragment */
+
+
+/**********************************************************************/
+
+%fragment("NumPy_Array_Requirements", "header",
+ fragment="NumPy_Backward_Compatibility",
+ fragment="NumPy_Macros")
+{
+ /* Test whether a python object is contiguous. If array is
+ * contiguous, return 1. Otherwise, set the python error string and
+ * return 0.
+ */
+ int require_contiguous(PyArrayObject* ary)
+ {
+ int contiguous = 1;
+ if (!array_is_contiguous(ary))
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "Array must be contiguous. A non-contiguous array was given");
+ contiguous = 0;
+ }
+ return contiguous;
+ }
+
+ /* Require that a numpy array is not byte-swapped. If the array is
+ * not byte-swapped, return 1. Otherwise, set the python error string
+ * and return 0.
+ */
+ int require_native(PyArrayObject* ary)
+ {
+ int native = 1;
+ if (!array_is_native(ary))
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "Array must have native byteorder. "
+ "A byte-swapped array was given");
+ native = 0;
+ }
+ return native;
+ }
+
+ /* Require the given PyArrayObject to have a specified number of
+ * dimensions. If the array has the specified number of dimensions,
+ * return 1. Otherwise, set the python error string and return 0.
+ */
+ int require_dimensions(PyArrayObject* ary, int exact_dimensions)
+ {
+ int success = 1;
+ if (array_numdims(ary) != exact_dimensions)
+ {
+ PyErr_Format(PyExc_TypeError,
+ "Array must have %d dimensions. Given array has %d dimensions",
+ exact_dimensions, array_numdims(ary));
+ success = 0;
+ }
+ return success;
+ }
+
+ /* Require the given PyArrayObject to have one of a list of specified
+ * number of dimensions. If the array has one of the specified number
+ * of dimensions, return 1. Otherwise, set the python error string
+ * and return 0.
+ */
+ int require_dimensions_n(PyArrayObject* ary, int* exact_dimensions, int n)
+ {
+ int success = 0;
+ int i;
+ char dims_str[255] = "";
+ char s[255];
+ for (i = 0; i < n && !success; i++)
+ {
+ if (array_numdims(ary) == exact_dimensions[i])
+ {
+ success = 1;
+ }
+ }
+ if (!success)
+ {
+ for (i = 0; i < n-1; i++)
+ {
+ sprintf(s, "%d, ", exact_dimensions[i]);
+ strcat(dims_str,s);
+ }
+ sprintf(s, " or %d", exact_dimensions[n-1]);
+ strcat(dims_str,s);
+ PyErr_Format(PyExc_TypeError,
+ "Array must have %s dimensions. Given array has %d dimensions",
+ dims_str, array_numdims(ary));
+ }
+ return success;
+ }
+
+ /* Require the given PyArrayObject to have a specified shape. If the
+ * array has the specified shape, return 1. Otherwise, set the python
+ * error string and return 0.
+ */
+ int require_size(PyArrayObject* ary, npy_intp* size, int n)
+ {
+ int i;
+ int success = 1;
+ int len;
+ char desired_dims[255] = "[";
+ char s[255];
+ char actual_dims[255] = "[";
+ for(i=0; i < n;i++)
+ {
+ if (size[i] != -1 && size[i] != array_size(ary,i))
+ {
+ success = 0;
+ }
+ }
+ if (!success)
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (size[i] == -1)
+ {
+ sprintf(s, "*,");
+ }
+ else
+ {
+ sprintf(s, "%ld,", (long int)size[i]);
+ }
+ strcat(desired_dims,s);
+ }
+ len = strlen(desired_dims);
+ desired_dims[len-1] = ']';
+ for (i = 0; i < n; i++)
+ {
+ sprintf(s, "%ld,", (long int)array_size(ary,i));
+ strcat(actual_dims,s);
+ }
+ len = strlen(actual_dims);
+ actual_dims[len-1] = ']';
+ PyErr_Format(PyExc_TypeError,
+ "Array must have shape of %s. Given array has shape of %s",
+ desired_dims, actual_dims);
+ }
+ return success;
+ }
+
+ /* Require the given PyArrayObject to to be FORTRAN ordered. If the
+ * the PyArrayObject is already FORTRAN ordered, do nothing. Else,
+ * set the FORTRAN ordering flag and recompute the strides.
+ */
+ int require_fortran(PyArrayObject* ary)
+ {
+ int success = 1;
+ int nd = array_numdims(ary);
+ int i;
+ if (array_is_fortran(ary)) return success;
+ /* Set the FORTRAN ordered flag */
+ ary->flags = NPY_FARRAY;
+ /* Recompute the strides */
+ ary->strides[0] = ary->strides[nd-1];
+ for (i=1; i < nd; ++i)
+ ary->strides[i] = ary->strides[i-1] * array_size(ary,i-1);
+ return success;
+ }
+}
+
+/* Combine all NumPy fragments into one for convenience */
+%fragment("NumPy_Fragments", "header",
+ fragment="NumPy_Backward_Compatibility",
+ fragment="NumPy_Macros",
+ fragment="NumPy_Utilities",
+ fragment="NumPy_Object_to_Array",
+ fragment="NumPy_Array_Requirements") { }
+
+/* End John Hunter translation (with modifications by Bill Spotz)
+ */
+
+/* %numpy_typemaps() macro
+ *
+ * This macro defines a family of 42 typemaps that allow C arguments
+ * of the form
+ *
+ * (DATA_TYPE IN_ARRAY1[ANY])
+ * (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+ * (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+ *
+ * (DATA_TYPE IN_ARRAY2[ANY][ANY])
+ * (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+ * (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+ *
+ * (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+ * (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+ * (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+ *
+ * (DATA_TYPE INPLACE_ARRAY1[ANY])
+ * (DATA_TYPE* INPLACE_ARRAY1)
+ * (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+ * (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+ *
+ * (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+ * (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+ * (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+ *
+ * (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+ * (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3)
+ * (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ * (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3)
+ *
+ * (DATA_TYPE ARGOUT_ARRAY1[ANY])
+ * (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+ * (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+ *
+ * (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+ *
+ * (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+ *
+ * (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1)
+ * (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1)
+ *
+ * (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2)
+ * (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2)
+ *
+ * (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
+ * (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ * (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)
+ *
+ * where "DATA_TYPE" is any type supported by the NumPy module, and
+ * "DIM_TYPE" is any int-like type suitable for specifying dimensions.
+ * The difference between "ARRAY" typemaps and "FARRAY" typemaps is
+ * that the "FARRAY" typemaps expect FORTRAN ordering of
+ * multidimensional arrays. In python, the dimensions will not need
+ * to be specified (except for the "DATA_TYPE* ARGOUT_ARRAY1"
+ * typemaps). The IN_ARRAYs can be a numpy array or any sequence that
+ * can be converted to a numpy array of the specified type. The
+ * INPLACE_ARRAYs must be numpy arrays of the appropriate type. The
+ * ARGOUT_ARRAYs will be returned as new numpy arrays of the
+ * appropriate type.
+ *
+ * These typemaps can be applied to existing functions using the
+ * %apply directive. For example:
+ *
+ * %apply (double* IN_ARRAY1, int DIM1) {(double* series, int length)};
+ * double prod(double* series, int length);
+ *
+ * %apply (int DIM1, int DIM2, double* INPLACE_ARRAY2)
+ * {(int rows, int cols, double* matrix )};
+ * void floor(int rows, int cols, double* matrix, double f);
+ *
+ * %apply (double IN_ARRAY3[ANY][ANY][ANY])
+ * {(double tensor[2][2][2] )};
+ * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY])
+ * {(double low[2][2][2] )};
+ * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY])
+ * {(double upp[2][2][2] )};
+ * void luSplit(double tensor[2][2][2],
+ * double low[2][2][2],
+ * double upp[2][2][2] );
+ *
+ * or directly with
+ *
+ * double prod(double* IN_ARRAY1, int DIM1);
+ *
+ * void floor(int DIM1, int DIM2, double* INPLACE_ARRAY2, double f);
+ *
+ * void luSplit(double IN_ARRAY3[ANY][ANY][ANY],
+ * double ARGOUT_ARRAY3[ANY][ANY][ANY],
+ * double ARGOUT_ARRAY3[ANY][ANY][ANY]);
+ */
+
+%define %numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE)
+
+/************************/
+/* Input Array Typemaps */
+/************************/
+
+/* Typemap suite for (DATA_TYPE IN_ARRAY1[ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE IN_ARRAY1[ANY])
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE IN_ARRAY1[ANY])
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[1] = { $1_dim0 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 1) ||
+ !require_size(array, size, 1)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(freearg)
+ (DATA_TYPE IN_ARRAY1[ANY])
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[1] = { -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 1) ||
+ !require_size(array, size, 1)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[1] = {-1};
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 1) ||
+ !require_size(array, size, 1)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE IN_ARRAY2[ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE IN_ARRAY2[ANY][ANY])
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE IN_ARRAY2[ANY][ANY])
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { $1_dim0, $1_dim1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(freearg)
+ (DATA_TYPE IN_ARRAY2[ANY][ANY])
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_fortran_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[2] = { -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 2) ||
+ !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(freearg)
+ (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY])
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* IN_ARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_fortran_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3) | !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+%typemap(freearg)
+ (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* IN_FARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+{
+ $1 = is_array($input) || PySequence_Check($input);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+ (PyArrayObject* array=NULL, int is_new_object=0)
+{
+ npy_intp size[3] = { -1, -1, -1 };
+ array = obj_to_array_contiguous_force_conversion($input, DATA_TYPECODE,
+ &is_new_object);
+ if (!array || !require_dimensions(array, 3) ||
+ !require_size(array, size, 3) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+%typemap(freearg)
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3)
+{
+ if (is_new_object$argnum && array$argnum)
+ { Py_DECREF(array$argnum); }
+}
+
+/***************************/
+/* In-Place Array Typemaps */
+/***************************/
+
+/* Typemap suite for (DATA_TYPE INPLACE_ARRAY1[ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE INPLACE_ARRAY1[ANY])
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE INPLACE_ARRAY1[ANY])
+ (PyArrayObject* array=NULL)
+{
+ npy_intp size[1] = { $1_dim0 };
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_size(array, size, 1) ||
+ !require_contiguous(array) || !require_native(array)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY1)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY1)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1)
+ (PyArrayObject* array=NULL, int i=1)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = 1;
+ for (i=0; i < array_numdims(array); ++i) $2 *= array_size(array,i);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1)
+ (PyArrayObject* array=NULL, int i=0)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,1) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = 1;
+ for (i=0; i < array_numdims(array); ++i) $1 *= array_size(array,i);
+ $2 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE INPLACE_ARRAY2[ANY][ANY])
+ (PyArrayObject* array=NULL)
+{
+ npy_intp size[2] = { $1_dim0, $1_dim1 };
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_size(array, size, 2) ||
+ !require_contiguous(array) || !require_native(array)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array) ||
+ !require_native(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array)
+ || !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,2) || !require_contiguous(array) ||
+ !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY])
+ (PyArrayObject* array=NULL)
+{
+ npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 };
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_size(array, size, 3) ||
+ !require_contiguous(array) || !require_native(array)) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array) ||
+ !require_native(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* INPLACE_ARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array)
+ || !require_native(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+
+/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2,
+ * DIM_TYPE DIM3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array) ||
+ !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+ $2 = (DIM_TYPE) array_size(array,0);
+ $3 = (DIM_TYPE) array_size(array,1);
+ $4 = (DIM_TYPE) array_size(array,2);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3,
+ * DATA_TYPE* INPLACE_FARRAY3)
+ */
+%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY,
+ fragment="NumPy_Macros")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3)
+{
+ $1 = is_array($input) && PyArray_EquivTypenums(array_type($input),
+ DATA_TYPECODE);
+}
+%typemap(in,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3)
+ (PyArrayObject* array=NULL)
+{
+ array = obj_to_array_no_conversion($input, DATA_TYPECODE);
+ if (!array || !require_dimensions(array,3) || !require_contiguous(array)
+ || !require_native(array) || !require_fortran(array)) SWIG_fail;
+ $1 = (DIM_TYPE) array_size(array,0);
+ $2 = (DIM_TYPE) array_size(array,1);
+ $3 = (DIM_TYPE) array_size(array,2);
+ $4 = (DATA_TYPE*) array_data(array);
+}
+
+/*************************/
+/* Argout Array Typemaps */
+/*************************/
+
+/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY1[ANY])
+ */
+%typemap(in,numinputs=0,
+ fragment="NumPy_Backward_Compatibility,NumPy_Macros")
+ (DATA_TYPE ARGOUT_ARRAY1[ANY])
+ (PyObject * array = NULL)
+{
+ npy_intp dims[1] = { $1_dim0 };
+ array = PyArray_SimpleNew(1, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE ARGOUT_ARRAY1[ANY])
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+ */
+%typemap(in,numinputs=1,
+ fragment="NumPy_Fragments")
+ (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+ (PyObject * array = NULL)
+{
+ npy_intp dims[1];
+ if (!PyInt_Check($input))
+ {
+ const char* typestring = pytype_string($input);
+ PyErr_Format(PyExc_TypeError,
+ "Int dimension expected. '%s' given.",
+ typestring);
+ SWIG_fail;
+ }
+ $2 = (DIM_TYPE) PyInt_AsLong($input);
+ dims[0] = (npy_intp) $2;
+ array = PyArray_SimpleNew(1, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = (DATA_TYPE*) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1)
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+ */
+%typemap(in,numinputs=1,
+ fragment="NumPy_Fragments")
+ (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+ (PyObject * array = NULL)
+{
+ npy_intp dims[1];
+ if (!PyInt_Check($input))
+ {
+ const char* typestring = pytype_string($input);
+ PyErr_Format(PyExc_TypeError,
+ "Int dimension expected. '%s' given.",
+ typestring);
+ SWIG_fail;
+ }
+ $1 = (DIM_TYPE) PyInt_AsLong($input);
+ dims[0] = (npy_intp) $1;
+ array = PyArray_SimpleNew(1, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $2 = (DATA_TYPE*) array_data(array);
+}
+%typemap(argout)
+ (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1)
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+ */
+%typemap(in,numinputs=0,
+ fragment="NumPy_Backward_Compatibility,NumPy_Macros")
+ (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+ (PyObject * array = NULL)
+{
+ npy_intp dims[2] = { $1_dim0, $1_dim1 };
+ array = PyArray_SimpleNew(2, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY])
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+ */
+%typemap(in,numinputs=0,
+ fragment="NumPy_Backward_Compatibility,NumPy_Macros")
+ (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+ (PyObject * array = NULL)
+{
+ npy_intp dims[3] = { $1_dim0, $1_dim1, $1_dim2 };
+ array = PyArray_SimpleNew(3, dims, DATA_TYPECODE);
+ if (!array) SWIG_fail;
+ $1 = ($1_ltype) array_data(array);
+}
+%typemap(argout)
+ (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY])
+{
+ $result = SWIG_Python_AppendOutput($result,array$argnum);
+}
+
+/*****************************/
+/* Argoutview Array Typemaps */
+/*****************************/
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 )
+ (DATA_TYPE* data_temp , DIM_TYPE dim_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1)
+{
+ npy_intp dims[1] = { *$2 };
+ PyObject * array = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEW_ARRAY1)
+ (DIM_TYPE dim_temp, DATA_TYPE* data_temp )
+{
+ $1 = &dim_temp;
+ $2 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1)
+{
+ npy_intp dims[1] = { *$1 };
+ PyObject * array = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 )
+ (DATA_TYPE* data_temp , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+{
+ npy_intp dims[2] = { *$2, *$3 };
+ PyObject * array = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_ARRAY2)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp )
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2)
+{
+ npy_intp dims[2] = { *$1, *$2 };
+ PyObject * array = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 )
+ (DATA_TYPE* data_temp , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2)
+{
+ npy_intp dims[2] = { *$2, *$3 };
+ PyObject * obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || !require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_FARRAY2)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp )
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2)
+{
+ npy_intp dims[2] = { *$1, *$2 };
+ PyObject * obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || !require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2,
+ DIM_TYPE* DIM3)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ (DATA_TYPE* data_temp, DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+ $4 = &dim3_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+{
+ npy_intp dims[3] = { *$2, *$3, *$4 };
+ PyObject * array = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3,
+ DATA_TYPE** ARGOUTVIEW_ARRAY3)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp)
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &dim3_temp;
+ $4 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
+{
+ npy_intp dims[3] = { *$1, *$2, *$3 };
+ PyObject * array = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$3));
+ if (!array) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,array);
+}
+
+/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2,
+ DIM_TYPE* DIM3)
+ */
+%typemap(in,numinputs=0)
+ (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+ (DATA_TYPE* data_temp, DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp)
+{
+ $1 = &data_temp;
+ $2 = &dim1_temp;
+ $3 = &dim2_temp;
+ $4 = &dim3_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3)
+{
+ npy_intp dims[3] = { *$2, *$3, *$4 };
+ PyObject * obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3,
+ DATA_TYPE** ARGOUTVIEW_FARRAY3)
+ */
+%typemap(in,numinputs=0)
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)
+ (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp)
+{
+ $1 = &dim1_temp;
+ $2 = &dim2_temp;
+ $3 = &dim3_temp;
+ $4 = &data_temp;
+}
+%typemap(argout,
+ fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements")
+ (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)
+{
+ npy_intp dims[3] = { *$1, *$2, *$3 };
+ PyObject * obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$3));
+ PyArrayObject * array = (PyArrayObject*) obj;
+ if (!array || require_fortran(array)) SWIG_fail;
+ $result = SWIG_Python_AppendOutput($result,obj);
+}
+
+%enddef /* %numpy_typemaps() macro */
+/* *************************************************************** */
+
+/* Concrete instances of the %numpy_typemaps() macro: Each invocation
+ * below applies all of the typemaps above to the specified data type.
+ */
+%numpy_typemaps(signed char , NPY_BYTE , int)
+%numpy_typemaps(unsigned char , NPY_UBYTE , int)
+%numpy_typemaps(short , NPY_SHORT , int)
+%numpy_typemaps(unsigned short , NPY_USHORT , int)
+%numpy_typemaps(int , NPY_INT , int)
+%numpy_typemaps(unsigned int , NPY_UINT , int)
+%numpy_typemaps(long , NPY_LONG , int)
+%numpy_typemaps(unsigned long , NPY_ULONG , int)
+%numpy_typemaps(long long , NPY_LONGLONG , int)
+%numpy_typemaps(unsigned long long, NPY_ULONGLONG, int)
+%numpy_typemaps(float , NPY_FLOAT , int)
+%numpy_typemaps(double , NPY_DOUBLE , int)
+
+/* ***************************************************************
+ * The follow macro expansion does not work, because C++ bool is 4
+ * bytes and NPY_BOOL is 1 byte
+ *
+ * %numpy_typemaps(bool, NPY_BOOL, int)
+ */
+
+/* ***************************************************************
+ * On my Mac, I get the following warning for this macro expansion:
+ * 'swig/python detected a memory leak of type 'long double *', no destructor found.'
+ *
+ * %numpy_typemaps(long double, NPY_LONGDOUBLE, int)
+ */
+
+/* ***************************************************************
+ * Swig complains about a syntax error for the following macro
+ * expansions:
+ *
+ * %numpy_typemaps(complex float, NPY_CFLOAT , int)
+ *
+ * %numpy_typemaps(complex double, NPY_CDOUBLE, int)
+ *
+ * %numpy_typemaps(complex long double, NPY_CLONGDOUBLE, int)
+ */
+
+#endif /* SWIGPYTHON */
diff --git a/bindings/python/Makefile b/bindings/python/Makefile
new file mode 100644
index 0000000..e63c124
--- /dev/null
+++ b/bindings/python/Makefile
@@ -0,0 +1,186 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+modules.rst lilv.rst:
+ mkdir -p lilv
+ ln -s -t lilv ../lilv.py
+ sphinx-apidoc -o . lilv
+
+clean:
+ rm -rf $(BUILDDIR)/*
+ rm -f lilv/lilv.py
+ rm -rf lilv
+ rm -f lilv.rst
+ rm -f modules.rst
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml: modules.rst lilv.rst
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Lilv.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Lilv.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Lilv"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Lilv"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/bindings/python/conf.py b/bindings/python/conf.py
new file mode 100644
index 0000000..576919e
--- /dev/null
+++ b/bindings/python/conf.py
@@ -0,0 +1,263 @@
+# -*- coding: utf-8 -*-
+#
+# Lilv documentation build configuration file, created by
+# sphinx-quickstart on Sun Sep 4 18:25:58 2016.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.ifconfig',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Lilv'
+copyright = u'2016, David Robillard'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.24.2'
+# The full version, including alpha/beta/rc tags.
+release = '0.24.2'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#html_theme = ''
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+html_theme_options = { 'nosidebar': True }
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Lilvdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'Lilv.tex', u'Lilv Documentation',
+ u'David Robillard', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'lilv', u'Lilv Documentation',
+ [u'David Robillard'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'Lilv', u'Lilv Documentation',
+ u'David Robillard', 'Lilv', 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/bindings/python/index.rst b/bindings/python/index.rst
new file mode 100644
index 0000000..4616054
--- /dev/null
+++ b/bindings/python/index.rst
@@ -0,0 +1,9 @@
+Lilv Python Documentation
+=========================
+
+
+.. toctree::
+
+.. automodule:: lilv
+ :noindex:
+ :members:
diff --git a/bindings/python/lilv.py b/bindings/python/lilv.py
new file mode 100644
index 0000000..024bfe7
--- /dev/null
+++ b/bindings/python/lilv.py
@@ -0,0 +1,1775 @@
+"""Lilv Python interface"""
+
+__author__ = "David Robillard"
+__copyright__ = "Copyright 2016 David Robillard"
+__license__ = "ISC"
+__version__ = "0.22.1"
+__maintainer__ = "David Robillard"
+__email__ = "d@drobilla.net"
+__status__ = "Production"
+
+import ctypes
+import os
+import sys
+
+from ctypes import Structure, CDLL, POINTER, CFUNCTYPE
+from ctypes import c_bool, c_double, c_float, c_int, c_size_t, c_uint, c_uint32
+from ctypes import c_char, c_char_p, c_void_p
+from ctypes import byref
+
+# Load lilv library
+
+_lib = CDLL("liblilv-0.so")
+
+# Set namespaced aliases for all lilv functions
+
+class String(str):
+ # Wrapper for string parameters to pass as raw C UTF-8 strings
+ def from_param(cls, obj):
+ return obj.encode('utf-8')
+
+ from_param = classmethod(from_param)
+
+def _as_uri(obj):
+ if type(obj) in [Plugin, PluginClass, UI]:
+ return obj.get_uri()
+ else:
+ return obj
+
+free = _lib.lilv_free
+# uri_to_path = _lib.lilv_uri_to_path
+file_uri_parse = _lib.lilv_file_uri_parse
+new_uri = _lib.lilv_new_uri
+new_file_uri = _lib.lilv_new_file_uri
+new_string = _lib.lilv_new_string
+new_int = _lib.lilv_new_int
+new_float = _lib.lilv_new_float
+new_bool = _lib.lilv_new_bool
+node_free = _lib.lilv_node_free
+node_duplicate = _lib.lilv_node_duplicate
+node_equals = _lib.lilv_node_equals
+node_get_turtle_token = _lib.lilv_node_get_turtle_token
+node_is_uri = _lib.lilv_node_is_uri
+node_as_uri = _lib.lilv_node_as_uri
+node_is_blank = _lib.lilv_node_is_blank
+node_as_blank = _lib.lilv_node_as_blank
+node_is_literal = _lib.lilv_node_is_literal
+node_is_string = _lib.lilv_node_is_string
+node_as_string = _lib.lilv_node_as_string
+node_get_path = _lib.lilv_node_get_path
+node_is_float = _lib.lilv_node_is_float
+node_as_float = _lib.lilv_node_as_float
+node_is_int = _lib.lilv_node_is_int
+node_as_int = _lib.lilv_node_as_int
+node_is_bool = _lib.lilv_node_is_bool
+node_as_bool = _lib.lilv_node_as_bool
+plugin_classes_free = _lib.lilv_plugin_classes_free
+plugin_classes_size = _lib.lilv_plugin_classes_size
+plugin_classes_begin = _lib.lilv_plugin_classes_begin
+plugin_classes_get = _lib.lilv_plugin_classes_get
+plugin_classes_next = _lib.lilv_plugin_classes_next
+plugin_classes_is_end = _lib.lilv_plugin_classes_is_end
+plugin_classes_get_by_uri = _lib.lilv_plugin_classes_get_by_uri
+scale_points_free = _lib.lilv_scale_points_free
+scale_points_size = _lib.lilv_scale_points_size
+scale_points_begin = _lib.lilv_scale_points_begin
+scale_points_get = _lib.lilv_scale_points_get
+scale_points_next = _lib.lilv_scale_points_next
+scale_points_is_end = _lib.lilv_scale_points_is_end
+uis_free = _lib.lilv_uis_free
+uis_size = _lib.lilv_uis_size
+uis_begin = _lib.lilv_uis_begin
+uis_get = _lib.lilv_uis_get
+uis_next = _lib.lilv_uis_next
+uis_is_end = _lib.lilv_uis_is_end
+uis_get_by_uri = _lib.lilv_uis_get_by_uri
+nodes_free = _lib.lilv_nodes_free
+nodes_size = _lib.lilv_nodes_size
+nodes_begin = _lib.lilv_nodes_begin
+nodes_get = _lib.lilv_nodes_get
+nodes_next = _lib.lilv_nodes_next
+nodes_is_end = _lib.lilv_nodes_is_end
+nodes_get_first = _lib.lilv_nodes_get_first
+nodes_contains = _lib.lilv_nodes_contains
+nodes_merge = _lib.lilv_nodes_merge
+plugins_size = _lib.lilv_plugins_size
+plugins_begin = _lib.lilv_plugins_begin
+plugins_get = _lib.lilv_plugins_get
+plugins_next = _lib.lilv_plugins_next
+plugins_is_end = _lib.lilv_plugins_is_end
+plugins_get_by_uri = _lib.lilv_plugins_get_by_uri
+world_new = _lib.lilv_world_new
+world_set_option = _lib.lilv_world_set_option
+world_free = _lib.lilv_world_free
+world_load_all = _lib.lilv_world_load_all
+world_load_bundle = _lib.lilv_world_load_bundle
+world_load_specifications = _lib.lilv_world_load_specifications
+world_load_plugin_classes = _lib.lilv_world_load_plugin_classes
+world_unload_bundle = _lib.lilv_world_unload_bundle
+world_load_resource = _lib.lilv_world_load_resource
+world_unload_resource = _lib.lilv_world_unload_resource
+world_get_plugin_class = _lib.lilv_world_get_plugin_class
+world_get_plugin_classes = _lib.lilv_world_get_plugin_classes
+world_get_all_plugins = _lib.lilv_world_get_all_plugins
+world_find_nodes = _lib.lilv_world_find_nodes
+world_get = _lib.lilv_world_get
+world_ask = _lib.lilv_world_ask
+plugin_verify = _lib.lilv_plugin_verify
+plugin_get_uri = _lib.lilv_plugin_get_uri
+plugin_get_bundle_uri = _lib.lilv_plugin_get_bundle_uri
+plugin_get_data_uris = _lib.lilv_plugin_get_data_uris
+plugin_get_library_uri = _lib.lilv_plugin_get_library_uri
+plugin_get_name = _lib.lilv_plugin_get_name
+plugin_get_class = _lib.lilv_plugin_get_class
+plugin_get_value = _lib.lilv_plugin_get_value
+plugin_has_feature = _lib.lilv_plugin_has_feature
+plugin_get_supported_features = _lib.lilv_plugin_get_supported_features
+plugin_get_required_features = _lib.lilv_plugin_get_required_features
+plugin_get_optional_features = _lib.lilv_plugin_get_optional_features
+plugin_has_extension_data = _lib.lilv_plugin_has_extension_data
+plugin_get_extension_data = _lib.lilv_plugin_get_extension_data
+plugin_get_num_ports = _lib.lilv_plugin_get_num_ports
+plugin_get_port_ranges_float = _lib.lilv_plugin_get_port_ranges_float
+plugin_has_latency = _lib.lilv_plugin_has_latency
+plugin_get_latency_port_index = _lib.lilv_plugin_get_latency_port_index
+plugin_get_port_by_index = _lib.lilv_plugin_get_port_by_index
+plugin_get_port_by_symbol = _lib.lilv_plugin_get_port_by_symbol
+plugin_get_port_by_designation = _lib.lilv_plugin_get_port_by_designation
+plugin_get_project = _lib.lilv_plugin_get_project
+plugin_get_author_name = _lib.lilv_plugin_get_author_name
+plugin_get_author_email = _lib.lilv_plugin_get_author_email
+plugin_get_author_homepage = _lib.lilv_plugin_get_author_homepage
+plugin_is_replaced = _lib.lilv_plugin_is_replaced
+plugin_get_related = _lib.lilv_plugin_get_related
+port_get_node = _lib.lilv_port_get_node
+port_get_value = _lib.lilv_port_get_value
+port_get = _lib.lilv_port_get
+port_get_properties = _lib.lilv_port_get_properties
+port_has_property = _lib.lilv_port_has_property
+port_supports_event = _lib.lilv_port_supports_event
+port_get_index = _lib.lilv_port_get_index
+port_get_symbol = _lib.lilv_port_get_symbol
+port_get_name = _lib.lilv_port_get_name
+port_get_classes = _lib.lilv_port_get_classes
+port_is_a = _lib.lilv_port_is_a
+port_get_range = _lib.lilv_port_get_range
+port_get_scale_points = _lib.lilv_port_get_scale_points
+state_new_from_world = _lib.lilv_state_new_from_world
+state_new_from_file = _lib.lilv_state_new_from_file
+state_new_from_string = _lib.lilv_state_new_from_string
+state_new_from_instance = _lib.lilv_state_new_from_instance
+state_free = _lib.lilv_state_free
+state_equals = _lib.lilv_state_equals
+state_get_num_properties = _lib.lilv_state_get_num_properties
+state_get_plugin_uri = _lib.lilv_state_get_plugin_uri
+state_get_uri = _lib.lilv_state_get_uri
+state_get_label = _lib.lilv_state_get_label
+state_set_label = _lib.lilv_state_set_label
+state_set_metadata = _lib.lilv_state_set_metadata
+state_emit_port_values = _lib.lilv_state_emit_port_values
+state_restore = _lib.lilv_state_restore
+state_save = _lib.lilv_state_save
+state_to_string = _lib.lilv_state_to_string
+state_delete = _lib.lilv_state_delete
+scale_point_get_label = _lib.lilv_scale_point_get_label
+scale_point_get_value = _lib.lilv_scale_point_get_value
+plugin_class_get_parent_uri = _lib.lilv_plugin_class_get_parent_uri
+plugin_class_get_uri = _lib.lilv_plugin_class_get_uri
+plugin_class_get_label = _lib.lilv_plugin_class_get_label
+plugin_class_get_children = _lib.lilv_plugin_class_get_children
+plugin_instantiate = _lib.lilv_plugin_instantiate
+instance_free = _lib.lilv_instance_free
+plugin_get_uis = _lib.lilv_plugin_get_uis
+ui_get_uri = _lib.lilv_ui_get_uri
+ui_get_classes = _lib.lilv_ui_get_classes
+ui_is_a = _lib.lilv_ui_is_a
+ui_is_supported = _lib.lilv_ui_is_supported
+ui_get_bundle_uri = _lib.lilv_ui_get_bundle_uri
+ui_get_binary_uri = _lib.lilv_ui_get_binary_uri
+
+## LV2 types
+
+LV2_Handle = POINTER(None)
+LV2_URID_Map_Handle = POINTER(None)
+LV2_URID_Unmap_Handle = POINTER(None)
+LV2_URID = c_uint32
+
+class LV2_Feature(Structure):
+ __slots__ = [ 'URI', 'data' ]
+ _fields_ = [('URI', c_char_p),
+ ('data', POINTER(None))]
+
+class LV2_Descriptor(Structure):
+ __slots__ = [ 'URI',
+ 'instantiate',
+ 'connect_port',
+ 'activate',
+ 'run',
+ 'deactivate',
+ 'cleanup',
+ 'extension_data' ]
+
+LV2_Descriptor._fields_ = [
+ ('URI', c_char_p),
+ ('instantiate', CFUNCTYPE(LV2_Handle, POINTER(LV2_Descriptor),
+ c_double, c_char_p, POINTER(POINTER(LV2_Feature)))),
+ ('connect_port', CFUNCTYPE(None, LV2_Handle, c_uint32, POINTER(None))),
+ ('activate', CFUNCTYPE(None, LV2_Handle)),
+ ('run', CFUNCTYPE(None, LV2_Handle, c_uint32)),
+ ('deactivate', CFUNCTYPE(None, LV2_Handle)),
+ ('cleanup', CFUNCTYPE(None, LV2_Handle)),
+ ('extension_data', CFUNCTYPE(c_void_p, c_char_p)),
+]
+
+class LV2_URID_Map(Structure):
+ __slots__ = [ 'handle', 'map' ]
+ _fields_ = [
+ ('handle', LV2_URID_Map_Handle),
+ ('map', CFUNCTYPE(LV2_URID, LV2_URID_Map_Handle, c_char_p)),
+ ]
+
+class LV2_URID_Unmap(Structure):
+ __slots__ = [ 'handle', 'unmap' ]
+ _fields_ = [
+ ('handle', LV2_URID_Unmap_Handle),
+ ('unmap', CFUNCTYPE(c_char_p, LV2_URID_Unmap_Handle, LV2_URID)),
+ ]
+
+# Lilv types
+
+class Plugin(Structure):
+ """LV2 Plugin."""
+ def __init__(self, world, plugin):
+ self.world = world
+ self.plugin = plugin
+
+ def __eq__(self, other):
+ return self.get_uri() == other.get_uri()
+
+ def verify(self):
+ """Check if `plugin` is valid.
+
+ This is not a rigorous validator, but can be used to reject some malformed
+ plugins that could cause bugs (e.g. plugins with missing required fields).
+
+ Note that normal hosts do NOT need to use this - lilv does not
+ load invalid plugins into plugin lists. This is included for plugin
+ testing utilities, etc.
+ """
+ return plugin_verify(self.plugin)
+
+ def get_uri(self):
+ """Get the URI of `plugin`.
+
+ Any serialization that refers to plugins should refer to them by this.
+ Hosts SHOULD NOT save any filesystem paths, plugin indexes, etc. in saved
+ files pass save only the URI.
+
+ The URI is a globally unique identifier for one specific plugin. Two
+ plugins with the same URI are compatible in port signature, and should
+ be guaranteed to work in a compatible and consistent way. If a plugin
+ is upgraded in an incompatible way (eg if it has different ports), it
+ MUST have a different URI than it's predecessor.
+ """
+ return Node.wrap(node_duplicate(plugin_get_uri(self.plugin)))
+
+ def get_bundle_uri(self):
+ """Get the (resolvable) URI of the plugin's "main" bundle.
+
+ This returns the URI of the bundle where the plugin itself was found. Note
+ that the data for a plugin may be spread over many bundles, that is,
+ get_data_uris() may return URIs which are not within this bundle.
+
+ Typical hosts should not need to use this function.
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv.file_uri_parse().
+ """
+ return Node.wrap(node_duplicate(plugin_get_bundle_uri(self.plugin)))
+
+ def get_data_uris(self):
+ """Get the (resolvable) URIs of the RDF data files that define a plugin.
+
+ Typical hosts should not need to use this function.
+ Note this always returns fully qualified URIs. If you want local
+ filesystem paths, use lilv.file_uri_parse().
+ """
+ return Nodes(plugin_get_data_uris(self.plugin))
+
+ def get_library_uri(self):
+ """Get the (resolvable) URI of the shared library for `plugin`.
+
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv.file_uri_parse().
+ """
+ return Node.wrap(node_duplicate(plugin_get_library_uri(self.plugin)))
+
+ def get_name(self):
+ """Get the name of `plugin`.
+
+ This returns the name (doap:name) of the plugin. The name may be
+ translated according to the current locale, this value MUST NOT be used
+ as a plugin identifier (use the URI for that).
+ """
+ return Node.wrap(plugin_get_name(self.plugin))
+
+ def get_class(self):
+ """Get the class this plugin belongs to (e.g. Filters)."""
+ return PluginClass(plugin_get_class(self.plugin))
+
+ def get_value(self, predicate):
+ """Get a value associated with the plugin in a plugin's data files.
+
+ `predicate` must be either a URI or a QName.
+
+ Returns the ?object of all triples found of the form:
+
+ plugin-uri predicate ?object
+
+ May return None if the property was not found, or if object(s) is not
+ sensibly represented as a LilvNodes (e.g. blank nodes).
+ """
+ return Nodes(plugin_get_value(self.plugin, predicate.node))
+
+ def has_feature(self, feature_uri):
+ """Return whether a feature is supported by a plugin.
+
+ This will return true if the feature is an optional or required feature
+ of the plugin.
+ """
+ return plugin_has_feature(self.plugin, feature_uri.node)
+
+ def get_supported_features(self):
+ """Get the LV2 Features supported (required or optionally) by a plugin.
+
+ A feature is "supported" by a plugin if it is required OR optional.
+
+ Since required features have special rules the host must obey, this function
+ probably shouldn't be used by normal hosts. Using get_optional_features()
+ and get_required_features() separately is best in most cases.
+ """
+ return Nodes(plugin_get_supported_features(self.plugin))
+
+ def get_required_features(self):
+ """Get the LV2 Features required by a plugin.
+
+ If a feature is required by a plugin, hosts MUST NOT use the plugin if they do not
+ understand (or are unable to support) that feature.
+
+ All values returned here MUST be return plugin_(self.plugin)ed to the plugin's instantiate method
+ (along with data, if necessary, as defined by the feature specification)
+ or plugin instantiation will fail.
+ """
+ return Nodes(plugin_get_required_features(self.plugin))
+
+ def get_optional_features(self):
+ """Get the LV2 Features optionally supported by a plugin.
+
+ Hosts MAY ignore optional plugin features for whatever reasons. Plugins
+ MUST operate (at least somewhat) if they are instantiated without being
+ passed optional features.
+ """
+ return Nodes(plugin_get_optional_features(self.plugin))
+
+ def has_extension_data(self, uri):
+ """Return whether or not a plugin provides a specific extension data."""
+ return plugin_has_extension_data(self.plugin, uri.node)
+
+ def get_extension_data(self):
+ """Get a sequence of all extension data provided by a plugin.
+
+ This can be used to find which URIs get_extension_data()
+ will return a value for without instantiating the plugin.
+ """
+ return Nodes(plugin_get_extension_data(self.plugin))
+
+ def get_num_ports(self):
+ """Get the number of ports on this plugin."""
+ return plugin_get_num_ports(self.plugin)
+
+ # def get_port_ranges_float(self, min_values, max_values, def_values):
+ # """Get the port ranges (minimum, maximum and default values) for all ports.
+
+ # `min_values`, `max_values` and `def_values` must either point to an array
+ # of N floats, where N is the value returned by get_num_ports()
+ # for this plugin, or None. The elements of the array will be set to the
+ # the minimum, maximum and default values of the ports on this plugin,
+ # with array index corresponding to port index. If a port doesn't have a
+ # minimum, maximum or default value, or the port's type is not float, the
+ # corresponding array element will be set to NAN.
+
+ # This is a convenience method for the common case of getting the range of
+ # all float ports on a plugin, and may be significantly faster than
+ # repeated calls to Port.get_range().
+ # """
+ # plugin_get_port_ranges_float(self.plugin, min_values, max_values, def_values)
+
+ def get_num_ports_of_class(self, *args):
+ """Get the number of ports on this plugin that are members of some class(es)."""
+ args = list(map(lambda x: x.node, args))
+ args += (None,)
+ return plugin_get_num_ports_of_class(self.plugin, *args)
+
+ def has_latency(self):
+ """Return whether or not the plugin introduces (and reports) latency.
+
+ The index of the latency port can be found with
+ get_latency_port() ONLY if this function returns true.
+ """
+ return plugin_has_latency(self.plugin)
+
+ def get_latency_port_index(self):
+ """Return the index of the plugin's latency port.
+
+ Returns None if the plugin has no latency port.
+
+ Any plugin that introduces unwanted latency that should be compensated for
+ (by hosts with the ability/need) MUST provide this port, which is a control
+ rate output port that reports the latency for each cycle in frames.
+ """
+ return plugin_get_latency_port_index(self.plugin) if self.has_latency() else None
+
+ def get_port(self, key):
+ """Get a port on `plugin` by index or symbol."""
+ if type(key) == int:
+ return self.get_port_by_index(key)
+ else:
+ return self.get_port_by_symbol(key)
+
+ def get_port_by_index(self, index):
+ """Get a port on `plugin` by `index`."""
+ return Port.wrap(self, plugin_get_port_by_index(self.plugin, index))
+
+ def get_port_by_symbol(self, symbol):
+ """Get a port on `plugin` by `symbol`.
+
+ Note this function is slower than get_port_by_index(),
+ especially on plugins with a very large number of ports.
+ """
+ if type(symbol) == str:
+ symbol = self.world.new_string(symbol)
+ return Port.wrap(self, plugin_get_port_by_symbol(self.plugin, symbol.node))
+
+ def get_port_by_designation(self, port_class, designation):
+ """Get a port on `plugin` by its lv2:designation.
+
+ The designation of a port describes the meaning, assignment, allocation or
+ role of the port, e.g. "left channel" or "gain". If found, the port with
+ matching `port_class` and `designation` is be returned, otherwise None is
+ returned. The `port_class` can be used to distinguish the input and output
+ ports for a particular designation. If `port_class` is None, any port with
+ the given designation will be returned.
+ """
+ return Port.wrap(self,
+ plugin_get_port_by_designation(self.plugin,
+ port_class.node,
+ designation.node))
+
+ def get_project(self):
+ """Get the project the plugin is a part of.
+
+ More information about the project can be read via find_nodes(),
+ typically using properties from DOAP (e.g. doap:name).
+ """
+ return Node.wrap(plugin_get_project(self.plugin))
+
+ def get_author_name(self):
+ """Get the full name of the plugin's author.
+
+ Returns None if author name is not present.
+ """
+ return Node.wrap(plugin_get_author_name(self.plugin))
+
+ def get_author_email(self):
+ """Get the email address of the plugin's author.
+
+ Returns None if author email address is not present.
+ """
+ return Node.wrap(plugin_get_author_email(self.plugin))
+
+ def get_author_homepage(self):
+ """Get the address of the plugin author's home page.
+
+ Returns None if author homepage is not present.
+ """
+ return Node.wrap(plugin_get_author_homepage(self.plugin))
+
+ def is_replaced(self):
+ """Return true iff `plugin` has been replaced by another plugin.
+
+ The plugin will still be usable, but hosts should hide them from their
+ user interfaces to prevent users from using deprecated plugins.
+ """
+ return plugin_is_replaced(self.plugin)
+
+ def get_related(self, resource_type):
+ """Get the resources related to `plugin` with lv2:appliesTo.
+
+ Some plugin-related resources are not linked directly to the plugin with
+ rdfs:seeAlso and thus will not be automatically loaded along with the plugin
+ data (usually for performance reasons). All such resources of the given @c
+ type related to `plugin` can be accessed with this function.
+
+ If `resource_type` is None, all such resources will be returned, regardless of type.
+
+ To actually load the data for each returned resource, use world.load_resource().
+ """
+ return Nodes(plugin_get_related(self.plugin, resource_type))
+
+ def get_uis(self):
+ """Get all UIs for `plugin`."""
+ return UIs(plugin_get_uis(self.plugin))
+
+class PluginClass(Structure):
+ """Plugin Class (type/category)."""
+ def __init__(self, plugin_class):
+ self.plugin_class = plugin_class
+
+ def __str__(self):
+ return self.get_uri().__str__()
+
+ def get_parent_uri(self):
+ """Get the URI of this class' superclass.
+
+ May return None if class has no parent.
+ """
+ return Node.wrap(node_duplicate(plugin_class_get_parent_uri(self.plugin_class)))
+
+ def get_uri(self):
+ """Get the URI of this plugin class."""
+ return Node.wrap(node_duplicate(plugin_class_get_uri(self.plugin_class)))
+
+ def get_label(self):
+ """Get the label of this plugin class, ie "Oscillators"."""
+ return Node.wrap(node_duplicate(plugin_class_get_label(self.plugin_class)))
+
+ def get_children(self):
+ """Get the subclasses of this plugin class."""
+ return PluginClasses(plugin_class_get_children(self.plugin_class))
+
+class Port(Structure):
+ """Port on a Plugin."""
+ @classmethod
+ def wrap(cls, plugin, port):
+ return Port(plugin, port) if plugin and port else None
+
+ def __init__(self, plugin, port):
+ self.plugin = plugin
+ self.port = port
+
+ def get_node(self):
+ """Get the RDF node of `port`.
+
+ Ports nodes may be may be URIs or blank nodes.
+ """
+ return Node.wrap(node_duplicate(port_get_node(self.plugin, self.port)))
+
+ def get_value(self, predicate):
+ """Port analog of Plugin.get_value()."""
+ return Nodes(port_get_value(self.plugin.plugin, self.port, predicate.node))
+
+ def get(self, predicate):
+ """Get a single property value of a port.
+
+ This is equivalent to lilv_nodes_get_first(lilv_port_get_value(...)) but is
+ simpler to use in the common case of only caring about one value. The
+ caller is responsible for freeing the returned node.
+ """
+ return Node.wrap(port_get(self.plugin.plugin, self.port, predicate.node))
+
+ def get_properties(self):
+ """Return the LV2 port properties of a port."""
+ return Nodes(port_get_properties(self.plugin.plugin, self.port))
+
+ def has_property(self, property_uri):
+ """Return whether a port has a certain property."""
+ return port_has_property(self.plugin.plugin, self.port, property_uri.node)
+
+ def supports_event(self, event_type):
+ """Return whether a port supports a certain event type.
+
+ More precisely, this returns true iff the port has an atom:supports or an
+ ev:supportsEvent property with `event_type` as the value.
+ """
+ return port_supports_event(self.plugin.plugin, self.port, event_type.node)
+
+ def get_index(self):
+ """Get the index of a port.
+
+ The index is only valid for the life of the plugin and may change between
+ versions. For a stable identifier, use the symbol.
+ """
+ return port_get_index(self.plugin.plugin, self.port)
+
+ def get_symbol(self):
+ """Get the symbol of a port.
+
+ The 'symbol' is a short string, a valid C identifier.
+ """
+ return Node.wrap(node_duplicate(port_get_symbol(self.plugin.plugin, self.port)))
+
+ def get_name(self):
+ """Get the name of a port.
+
+ This is guaranteed to return the untranslated name (the doap:name in the
+ data file without a language tag).
+ """
+ return Node.wrap(port_get_name(self.plugin.plugin, self.port))
+
+ def get_classes(self):
+ """Get all the classes of a port.
+
+ This can be used to determine if a port is an input, output, audio,
+ control, midi, etc, etc, though it's simpler to use is_a().
+ The returned list does not include lv2:Port, which is implied.
+ Returned value is shared and must not be destroyed by caller.
+ """
+ return Nodes(port_get_classes(self.plugin.plugin, self.port))
+
+ def is_a(self, port_class):
+ """Determine if a port is of a given class (input, output, audio, etc).
+
+ For convenience/performance/extensibility reasons, hosts are expected to
+ create a LilvNode for each port class they "care about". Well-known type
+ URI strings are defined (e.g. LILV_URI_INPUT_PORT) for convenience, but
+ this function is designed so that Lilv is usable with any port types
+ without requiring explicit support in Lilv.
+ """
+ return port_is_a(self.plugin.plugin, self.port, port_class.node)
+
+ def get_range(self):
+ """Return the default, minimum, and maximum values of a port as a tuple."""
+ pdef = POINTER(Node)()
+ pmin = POINTER(Node)()
+ pmax = POINTER(Node)()
+ port_get_range(self.plugin.plugin, self.port, byref(pdef), byref(pmin), byref(pmax))
+ return (Node(pdef.contents) if pdef else None,
+ Node(pmin.contents) if pmin else None,
+ Node(pmax.contents) if pmax else None)
+
+ def get_scale_points(self):
+ """Get the scale points (enumeration values) of a port.
+
+ This returns a collection of 'interesting' named values of a port
+ (e.g. appropriate entries for a UI selector associated with this port).
+ Returned value may be None if `port` has no scale points.
+ """
+ return ScalePoints(port_get_scale_points(self.plugin.plugin, self.port))
+
+class ScalePoint(Structure):
+ """Scale point (detent)."""
+ def __init__(self, point):
+ self.point = point
+
+ def get_label(self):
+ """Get the label of this scale point (enumeration value)."""
+ return Node.wrap(scale_point_get_label(self.point))
+
+ def get_value(self):
+ """Get the value of this scale point (enumeration value)."""
+ return Node.wrap(scale_point_get_value(self.point))
+
+class UI(Structure):
+ """Plugin UI."""
+ def __init__(self, ui):
+ self.ui = ui
+
+ def __str__(self):
+ return str(self.get_uri())
+
+ def __eq__(self, other):
+ return self.get_uri() == _as_uri(other)
+
+ def get_uri(self):
+ """Get the URI of a Plugin UI."""
+ return Node.wrap(node_duplicate(ui_get_uri(self.ui)))
+
+ def get_classes(self):
+ """Get the types (URIs of RDF classes) of a Plugin UI.
+
+ Note that in most cases is_supported() should be used, which avoids
+ the need to use this function (and type specific logic).
+ """
+ return Nodes(ui_get_classes(self.ui))
+
+ def is_a(self, class_uri):
+ """Check whether a plugin UI has a given type."""
+ return ui_is_a(self.ui, class_uri.node)
+
+ def get_bundle_uri(self):
+ """Get the URI of the UI's bundle."""
+ return Node.wrap(node_duplicate(ui_get_bundle_uri(self.ui)))
+
+ def get_binary_uri(self):
+ """Get the URI for the UI's shared library."""
+ return Node.wrap(node_duplicate(ui_get_binary_uri(self.ui)))
+
+class Node(Structure):
+ """Data node (URI, string, integer, etc.).
+
+ A Node can be converted to the corresponding Python datatype, and all nodes
+ can be converted to strings, for example::
+
+ >>> world = lilv.World()
+ >>> i = world.new_int(42)
+ >>> print(i)
+ 42
+ >>> int(i) * 2
+ 84
+ """
+ @classmethod
+ def wrap(cls, node):
+ return Node(node) if node else None
+
+ def __init__(self, node):
+ self.node = node
+
+ def __del__(self):
+ if hasattr(self, 'node'):
+ node_free(self.node)
+
+ def __eq__(self, other):
+ otype = type(other)
+ if otype in [str, int, float]:
+ return otype(self) == other
+ return node_equals(self.node, other.node)
+
+ def __ne__(self, other):
+ return not node_equals(self.node, other.node)
+
+ def __str__(self):
+ return node_as_string(self.node).decode('utf-8')
+
+ def __int__(self):
+ if not self.is_int():
+ raise ValueError('node %s is not an integer' % str(self))
+ return node_as_int(self.node)
+
+ def __float__(self):
+ if not self.is_float():
+ raise ValueError('node %s is not a float' % str(self))
+ return node_as_float(self.node)
+
+ def __bool__(self):
+ if not self.is_bool():
+ raise ValueError('node %s is not a bool' % str(self))
+ return node_as_bool(self.node)
+ __nonzero__ = __bool__
+
+ def get_turtle_token(self):
+ """Return this value as a Turtle/SPARQL token."""
+ return node_get_turtle_token(self.node).decode('utf-8')
+
+ def is_uri(self):
+ """Return whether the value is a URI (resource)."""
+ return node_is_uri(self.node)
+
+ def is_blank(self):
+ """Return whether the value is a blank node (resource with no URI)."""
+ return node_is_blank(self.node)
+
+ def is_literal(self):
+ """Return whether this value is a literal (i.e. not a URI)."""
+ return node_is_literal(self.node)
+
+ def is_string(self):
+ """Return whether this value is a string literal.
+
+ Returns true if value is a string value (and not numeric).
+ """
+ return node_is_string(self.node)
+
+ def get_path(self, hostname=None):
+ """Return the path of a file URI node.
+
+ Returns None if value is not a file URI."""
+ return node_get_path(self.node, hostname).decode('utf-8')
+
+ def is_float(self):
+ """Return whether this value is a decimal literal."""
+ return node_is_float(self.node)
+
+ def is_int(self):
+ """Return whether this value is an integer literal."""
+ return node_is_int(self.node)
+
+ def is_bool(self):
+ """Return whether this value is a boolean."""
+ return node_is_bool(self.node)
+
+class Iter(Structure):
+ """Collection iterator."""
+ def __init__(self, collection, iterator, constructor, iter_get, iter_next, iter_is_end):
+ self.collection = collection
+ self.iterator = iterator
+ self.constructor = constructor
+ self.iter_get = iter_get
+ self.iter_next = iter_next
+ self.iter_is_end = iter_is_end
+
+ def get(self):
+ """Get the current item."""
+ return self.constructor(self.iter_get(self.collection, self.iterator))
+
+ def next(self):
+ """Move to and return the next item."""
+ if self.is_end():
+ raise StopIteration
+ elem = self.get()
+ self.iterator = self.iter_next(self.collection, self.iterator)
+ return elem
+
+ def is_end(self):
+ """Return true if the end of the collection has been reached."""
+ return self.iter_is_end(self.collection, self.iterator)
+
+ __next__ = next
+
+class Collection(Structure):
+ # Base class for all lilv collection wrappers.
+ def __init__(self, collection, iter_begin, constructor, iter_get, iter_next, is_end):
+ self.collection = collection
+ self.constructor = constructor
+ self.iter_begin = iter_begin
+ self.iter_get = iter_get
+ self.iter_next = iter_next
+ self.is_end = is_end
+
+ def __iter__(self):
+ return Iter(self.collection, self.iter_begin(self.collection), self.constructor,
+ self.iter_get, self.iter_next, self.is_end)
+
+ def __getitem__(self, index):
+ if index >= len(self):
+ raise IndexError
+ pos = 0
+ for i in self:
+ if pos == index:
+ return i
+ pos += 1
+
+ def begin(self):
+ return self.__iter__()
+
+ def get(self, iterator):
+ return iterator.get()
+
+class Plugins(Collection):
+ """Collection of plugins."""
+ def __init__(self, world, collection):
+ def constructor(plugin):
+ return Plugin(world, plugin)
+
+ super(Plugins, self).__init__(collection, plugins_begin, constructor, plugins_get, plugins_next, plugins_is_end)
+ self.world = world
+
+ def __contains__(self, key):
+ return bool(self.get_by_uri(_as_uri(key)))
+
+ def __len__(self):
+ return plugins_size(self.collection)
+
+ def __getitem__(self, key):
+ if type(key) == int:
+ return super(Plugins, self).__getitem__(key)
+ return self.get_by_uri(key)
+
+ def get_by_uri(self, uri):
+ plugin = plugins_get_by_uri(self.collection, uri.node)
+ return Plugin(self.world, plugin) if plugin else None
+
+class PluginClasses(Collection):
+ """Collection of plugin classes."""
+ def __init__(self, collection):
+ super(PluginClasses, self).__init__(
+ collection, plugin_classes_begin, PluginClass,
+ plugin_classes_get, plugin_classes_next, plugin_classes_is_end)
+
+ def __contains__(self, key):
+ return bool(self.get_by_uri(_as_uri(key)))
+
+ def __len__(self):
+ return plugin_classes_size(self.collection)
+
+ def __getitem__(self, key):
+ if type(key) == int:
+ return super(PluginClasses, self).__getitem__(key)
+ return self.get_by_uri(key)
+
+ def get_by_uri(self, uri):
+ plugin_class = plugin_classes_get_by_uri(self.collection, uri.node)
+ return PluginClass(plugin_class) if plugin_class else None
+
+class ScalePoints(Collection):
+ """Collection of scale points."""
+ def __init__(self, collection):
+ super(ScalePoints, self).__init__(
+ collection, scale_points_begin, ScalePoint,
+ scale_points_get, scale_points_next, scale_points_is_end)
+
+ def __len__(self):
+ return scale_points_size(self.collection)
+
+class UIs(Collection):
+ """Collection of plugin UIs."""
+ def __init__(self, collection):
+ super(UIs, self).__init__(collection, uis_begin, UI,
+ uis_get, uis_next, uis_is_end)
+
+ def __contains__(self, uri):
+ return bool(self.get_by_uri(_as_uri(uri)))
+
+ def __len__(self):
+ return uis_size(self.collection)
+
+ def __getitem__(self, key):
+ if type(key) == int:
+ return super(UIs, self).__getitem__(key)
+ return self.get_by_uri(key)
+
+ def get_by_uri(self, uri):
+ ui = uis_get_by_uri(self.collection, uri.node)
+ return UI(ui) if ui else None
+
+class Nodes(Collection):
+ """Collection of data nodes."""
+ @classmethod
+ def constructor(ignore, node):
+ return Node(node_duplicate(node))
+
+ def __init__(self, collection):
+ super(Nodes, self).__init__(collection, nodes_begin, Nodes.constructor,
+ nodes_get, nodes_next, nodes_is_end)
+
+ def __contains__(self, value):
+ return nodes_contains(self.collection, value.node)
+
+ def __len__(self):
+ return nodes_size(self.collection)
+
+ def merge(self, b):
+ return Nodes(nodes_merge(self.collection, b.collection))
+
+class Namespace():
+ """Namespace prefix.
+
+ Use attribute syntax to easily create URIs within this namespace, for
+ example::
+
+ >>> world = lilv.World()
+ >>> ns = Namespace(world, "http://example.org/")
+ >>> print(ns.foo)
+ http://example.org/foo
+ """
+ def __init__(self, world, prefix):
+ self.world = world
+ self.prefix = prefix
+
+ def __eq__(self, other):
+ return str(self) == str(other)
+
+ def __str__(self):
+ return self.prefix
+
+ def __getattr__(self, suffix):
+ return self.world.new_uri(self.prefix + suffix)
+
+class World(Structure):
+ """Library context.
+
+ Includes a set of namespaces as the instance variable `ns`, so URIs can be constructed like::
+
+ uri = world.ns.lv2.Plugin
+
+ :ivar ns: Common LV2 namespace prefixes: atom, doap, foaf, lilv, lv2, midi, owl, rdf, rdfs, ui, xsd.
+ """
+ def __init__(self):
+ world = self
+
+ # Define Namespaces class locally so available prefixes are documented
+ class Namespaces():
+ """Set of namespaces.
+
+ Use to easily construct uris, like: ns.lv2.InputPort"""
+
+ atom = Namespace(world, 'http://lv2plug.in/ns/ext/atom#')
+ doap = Namespace(world, 'http://usefulinc.com/ns/doap#')
+ foaf = Namespace(world, 'http://xmlns.com/foaf/0.1/')
+ lilv = Namespace(world, 'http://drobilla.net/ns/lilv#')
+ lv2 = Namespace(world, 'http://lv2plug.in/ns/lv2core#')
+ midi = Namespace(world, 'http://lv2plug.in/ns/ext/midi#')
+ owl = Namespace(world, 'http://www.w3.org/2002/07/owl#')
+ rdf = Namespace(world, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#')
+ rdfs = Namespace(world, 'http://www.w3.org/2000/01/rdf-schema#')
+ ui = Namespace(world, 'http://lv2plug.in/ns/extensions/ui#')
+ xsd = Namespace(world, 'http://www.w3.org/2001/XMLSchema#')
+
+ self.world = _lib.lilv_world_new()
+ self.ns = Namespaces()
+
+ def __del__(self):
+ world_free(self.world)
+
+ def set_option(self, uri, value):
+ """Set a world option.
+
+ Currently recognized options:
+ lilv.OPTION_FILTER_LANG
+ lilv.OPTION_DYN_MANIFEST
+ """
+ return world_set_option(self, uri, value.node)
+
+ def load_all(self):
+ """Load all installed LV2 bundles on the system.
+
+ This is the recommended way for hosts to load LV2 data. It implements the
+ established/standard best practice for discovering all LV2 data on the
+ system. The environment variable LV2_PATH may be used to control where
+ this function will look for bundles.
+
+ Hosts should use this function rather than explicitly load bundles, except
+ in special circumstances (e.g. development utilities, or hosts that ship
+ with special plugin bundles which are installed to a known location).
+ """
+ world_load_all(self.world)
+
+ def load_bundle(self, bundle_uri):
+ """Load a specific bundle.
+
+ `bundle_uri` must be a fully qualified URI to the bundle directory,
+ with the trailing slash, eg. file:///usr/lib/lv2/foo.lv2/
+
+ Normal hosts should not need this function (use load_all()).
+
+ Hosts MUST NOT attach any long-term significance to bundle paths
+ (e.g. in save files), since there are no guarantees they will remain
+ unchanged between (or even during) program invocations. Plugins (among
+ other things) MUST be identified by URIs (not paths) in save files.
+ """
+ world_load_bundle(self.world, bundle_uri.node)
+
+ def load_specifications(self):
+ """Load all specifications from currently loaded bundles.
+
+ This is for hosts that explicitly load specific bundles, its use is not
+ necessary when using load_all(). This function parses the
+ specifications and adds them to the model.
+ """
+ world_load_specifications(self.world)
+
+ def load_plugin_classes(self):
+ """Load all plugin classes from currently loaded specifications.
+
+ Must be called after load_specifications(). This is for hosts
+ that explicitly load specific bundles, its use is not necessary when using
+ load_all().
+ """
+ world_load_plugin_classes(self.world)
+
+ def unload_bundle(self, bundle_uri):
+ """Unload a specific bundle.
+
+ This unloads statements loaded by load_bundle(). Note that this
+ is not necessarily all information loaded from the bundle. If any resources
+ have been separately loaded with load_resource(), they must be
+ separately unloaded with unload_resource().
+ """
+ return world_unload_bundle(self.world, bundle_uri.node)
+
+ def load_resource(self, resource):
+ """Load all the data associated with the given `resource`.
+
+ The resource must be a subject (i.e. a URI or a blank node).
+ Returns the number of files parsed, or -1 on error.
+
+ All accessible data files linked to `resource` with rdfs:seeAlso will be
+ loaded into the world model.
+ """
+ return world_load_resource(self.world, _as_uri(resource).node)
+
+ def unload_resource(self, resource):
+ """Unload all the data associated with the given `resource`.
+
+ The resource must be a subject (i.e. a URI or a blank node).
+
+ This unloads all data loaded by a previous call to
+ load_resource() with the given `resource`.
+ """
+ return world_unload_resource(self.world, _as_uri(resource).node)
+
+ def get_plugin_class(self):
+ """Get the parent of all other plugin classes, lv2:Plugin."""
+ return PluginClass(world_get_plugin_class(self.world))
+
+ def get_plugin_classes(self):
+ """Return a list of all found plugin classes."""
+ return PluginClasses(world_get_plugin_classes(self.world))
+
+ def get_all_plugins(self):
+ """Return a list of all found plugins.
+
+ The returned list contains just enough references to query
+ or instantiate plugins. The data for a particular plugin will not be
+ loaded into memory until a call to an lilv_plugin_* function results in
+ a query (at which time the data is cached with the LilvPlugin so future
+ queries are very fast).
+
+ The returned list and the plugins it contains are owned by `world`
+ and must not be freed by caller.
+ """
+ return Plugins(self, _lib.lilv_world_get_all_plugins(self.world))
+
+ def find_nodes(self, subject, predicate, obj):
+ """Find nodes matching a triple pattern.
+
+ Either `subject` or `object` may be None (i.e. a wildcard), but not both.
+ Returns all matches for the wildcard field, or None.
+ """
+ return Nodes(world_find_nodes(self.world,
+ subject.node if subject is not None else None,
+ predicate.node if predicate is not None else None,
+ obj.node if obj is not None else None))
+
+ def get(self, subject, predicate, obj):
+ """Find a single node that matches a pattern.
+
+ Exactly one of `subject`, `predicate`, `object` must be None.
+
+ Returns the first matching node, or None if no matches are found.
+ """
+ return Node.wrap(world_get(self.world,
+ subject.node if subject is not None else None,
+ predicate.node if predicate is not None else None,
+ obj.node if obj is not None else None))
+
+ def ask(self, subject, predicate, obj):
+ """Return true iff a statement matching a certain pattern exists.
+
+ This is useful for checking if particular statement exists without having to
+ bother with collections and memory management.
+ """
+ return world_ask(self.world,
+ subject.node if subject is not None else None,
+ predicate.node if predicate is not None else None,
+ obj.node if obj is not None else None)
+
+ def new_uri(self, uri):
+ """Create a new URI node."""
+ return Node.wrap(_lib.lilv_new_uri(self.world, uri))
+
+ def new_file_uri(self, host, path):
+ """Create a new file URI node. The host may be None."""
+ return Node.wrap(_lib.lilv_new_file_uri(self.world, host, path))
+
+ def new_string(self, string):
+ """Create a new string node."""
+ return Node.wrap(_lib.lilv_new_string(self.world, string))
+
+ def new_int(self, val):
+ """Create a new int node."""
+ return Node.wrap(_lib.lilv_new_int(self.world, val))
+
+ def new_float(self, val):
+ """Create a new float node."""
+ return Node.wrap(_lib.lilv_new_float(self.world, val))
+
+ def new_bool(self, val):
+ """Create a new bool node."""
+ return Node.wrap(_lib.lilv_new_bool(self.world, val))
+
+class Instance(Structure):
+ """Plugin instance."""
+ __slots__ = [ 'lv2_descriptor', 'lv2_handle', 'pimpl', 'plugin', 'rate', 'instance' ]
+ _fields_ = [
+ ('lv2_descriptor', POINTER(LV2_Descriptor)),
+ ('lv2_handle', LV2_Handle),
+ ('pimpl', POINTER(None)),
+ ]
+
+ def __init__(self, plugin, rate, features=None):
+ self.plugin = plugin
+ self.rate = rate
+ self.instance = plugin_instantiate(plugin.plugin, rate, features)
+
+ def get_uri(self):
+ """Get the URI of the plugin which `instance` is an instance of.
+
+ Returned string is shared and must not be modified or deleted.
+ """
+ return self.get_descriptor().URI
+
+ def connect_port(self, port_index, data):
+ """Connect a port to a data location.
+
+ This may be called regardless of whether the plugin is activated,
+ activation and deactivation does not destroy port connections.
+ """
+ import numpy
+ if data is None:
+ self.get_descriptor().connect_port(
+ self.get_handle(),
+ port_index,
+ data)
+ elif type(data) == numpy.ndarray:
+ self.get_descriptor().connect_port(
+ self.get_handle(),
+ port_index,
+ data.ctypes.data_as(POINTER(c_float)))
+ else:
+ raise Exception("Unsupported data type")
+
+ def activate(self):
+ """Activate a plugin instance.
+
+ This resets all state information in the plugin, except for port data
+ locations (as set by connect_port()). This MUST be called
+ before calling run().
+ """
+ if self.get_descriptor().activate:
+ self.get_descriptor().activate(self.get_handle())
+
+ def run(self, sample_count):
+ """Run `instance` for `sample_count` frames.
+
+ If the hint lv2:hardRTCapable is set for this plugin, this function is
+ guaranteed not to block.
+ """
+ self.get_descriptor().run(self.get_handle(), sample_count)
+
+ def deactivate(self):
+ """Deactivate a plugin instance.
+
+ Note that to run the plugin after this you must activate it, which will
+ reset all state information (except port connections).
+ """
+ if self.get_descriptor().deactivate:
+ self.get_descriptor().deactivate(self.get_handle())
+
+ def get_extension_data(self, uri):
+ """Get extension data from the plugin instance.
+
+ The type and semantics of the data returned is specific to the particular
+ extension, though in all cases it is shared and must not be deleted.
+ """
+ if self.get_descriptor().extension_data:
+ return self.get_descriptor().extension_data(str(uri))
+
+ def get_descriptor(self):
+ """Get the LV2_Descriptor of the plugin instance.
+
+ Normally hosts should not need to access the LV2_Descriptor directly,
+ use the lilv_instance_* functions.
+ """
+ return self.instance[0].lv2_descriptor[0]
+
+ def get_handle(self):
+ """Get the LV2_Handle of the plugin instance.
+
+ Normally hosts should not need to access the LV2_Handle directly,
+ use the lilv_instance_* functions.
+ """
+ return self.instance[0].lv2_handle
+
+class State(Structure):
+ """Plugin state (TODO)."""
+ pass
+
+class VariadicFunction(object):
+ # Wrapper for calling C variadic functions
+ def __init__(self, function, restype, argtypes):
+ self.function = function
+ self.function.restype = restype
+ self.argtypes = argtypes
+
+ def __call__(self, *args):
+ fixed_args = []
+ i = 0
+ for argtype in self.argtypes:
+ fixed_args.append(argtype.from_param(args[i]))
+ i += 1
+ return self.function(*fixed_args + list(args[i:]))
+
+# Set return and argument types for lilv C functions
+
+free.argtypes = [POINTER(None)]
+free.restype = None
+
+# uri_to_path.argtypes = [String]
+# uri_to_path.restype = c_char_p
+
+file_uri_parse.argtypes = [String, POINTER(POINTER(c_char))]
+file_uri_parse.restype = c_char_p
+
+new_uri.argtypes = [POINTER(World), String]
+new_uri.restype = POINTER(Node)
+
+new_file_uri.argtypes = [POINTER(World), c_char_p, String]
+new_file_uri.restype = POINTER(Node)
+
+new_string.argtypes = [POINTER(World), String]
+new_string.restype = POINTER(Node)
+
+new_int.argtypes = [POINTER(World), c_int]
+new_int.restype = POINTER(Node)
+
+new_float.argtypes = [POINTER(World), c_float]
+new_float.restype = POINTER(Node)
+
+new_bool.argtypes = [POINTER(World), c_bool]
+new_bool.restype = POINTER(Node)
+
+node_free.argtypes = [POINTER(Node)]
+node_free.restype = None
+
+node_duplicate.argtypes = [POINTER(Node)]
+node_duplicate.restype = POINTER(Node)
+
+node_equals.argtypes = [POINTER(Node), POINTER(Node)]
+node_equals.restype = c_bool
+
+node_get_turtle_token.argtypes = [POINTER(Node)]
+node_get_turtle_token.restype = c_char_p
+
+node_is_uri.argtypes = [POINTER(Node)]
+node_is_uri.restype = c_bool
+
+node_as_uri.argtypes = [POINTER(Node)]
+node_as_uri.restype = c_char_p
+
+node_is_blank.argtypes = [POINTER(Node)]
+node_is_blank.restype = c_bool
+
+node_as_blank.argtypes = [POINTER(Node)]
+node_as_blank.restype = c_char_p
+
+node_is_literal.argtypes = [POINTER(Node)]
+node_is_literal.restype = c_bool
+
+node_is_string.argtypes = [POINTER(Node)]
+node_is_string.restype = c_bool
+
+node_as_string.argtypes = [POINTER(Node)]
+node_as_string.restype = c_char_p
+
+node_get_path.argtypes = [POINTER(Node), POINTER(POINTER(c_char))]
+node_get_path.restype = c_char_p
+
+node_is_float.argtypes = [POINTER(Node)]
+node_is_float.restype = c_bool
+
+node_as_float.argtypes = [POINTER(Node)]
+node_as_float.restype = c_float
+
+node_is_int.argtypes = [POINTER(Node)]
+node_is_int.restype = c_bool
+
+node_as_int.argtypes = [POINTER(Node)]
+node_as_int.restype = c_int
+
+node_is_bool.argtypes = [POINTER(Node)]
+node_is_bool.restype = c_bool
+
+node_as_bool.argtypes = [POINTER(Node)]
+node_as_bool.restype = c_bool
+
+plugin_classes_free.argtypes = [POINTER(PluginClasses)]
+plugin_classes_free.restype = None
+
+plugin_classes_size.argtypes = [POINTER(PluginClasses)]
+plugin_classes_size.restype = c_uint
+
+plugin_classes_begin.argtypes = [POINTER(PluginClasses)]
+plugin_classes_begin.restype = POINTER(Iter)
+
+plugin_classes_get.argtypes = [POINTER(PluginClasses), POINTER(Iter)]
+plugin_classes_get.restype = POINTER(PluginClass)
+
+plugin_classes_next.argtypes = [POINTER(PluginClasses), POINTER(Iter)]
+plugin_classes_next.restype = POINTER(Iter)
+
+plugin_classes_is_end.argtypes = [POINTER(PluginClasses), POINTER(Iter)]
+plugin_classes_is_end.restype = c_bool
+
+plugin_classes_get_by_uri.argtypes = [POINTER(PluginClasses), POINTER(Node)]
+plugin_classes_get_by_uri.restype = POINTER(PluginClass)
+
+scale_points_free.argtypes = [POINTER(ScalePoints)]
+scale_points_free.restype = None
+
+scale_points_size.argtypes = [POINTER(ScalePoints)]
+scale_points_size.restype = c_uint
+
+scale_points_begin.argtypes = [POINTER(ScalePoints)]
+scale_points_begin.restype = POINTER(Iter)
+
+scale_points_get.argtypes = [POINTER(ScalePoints), POINTER(Iter)]
+scale_points_get.restype = POINTER(ScalePoint)
+
+scale_points_next.argtypes = [POINTER(ScalePoints), POINTER(Iter)]
+scale_points_next.restype = POINTER(Iter)
+
+scale_points_is_end.argtypes = [POINTER(ScalePoints), POINTER(Iter)]
+scale_points_is_end.restype = c_bool
+
+uis_free.argtypes = [POINTER(UIs)]
+uis_free.restype = None
+
+uis_size.argtypes = [POINTER(UIs)]
+uis_size.restype = c_uint
+
+uis_begin.argtypes = [POINTER(UIs)]
+uis_begin.restype = POINTER(Iter)
+
+uis_get.argtypes = [POINTER(UIs), POINTER(Iter)]
+uis_get.restype = POINTER(UI)
+
+uis_next.argtypes = [POINTER(UIs), POINTER(Iter)]
+uis_next.restype = POINTER(Iter)
+
+uis_is_end.argtypes = [POINTER(UIs), POINTER(Iter)]
+uis_is_end.restype = c_bool
+
+uis_get_by_uri.argtypes = [POINTER(UIs), POINTER(Node)]
+uis_get_by_uri.restype = POINTER(UI)
+
+nodes_free.argtypes = [POINTER(Nodes)]
+nodes_free.restype = None
+
+nodes_size.argtypes = [POINTER(Nodes)]
+nodes_size.restype = c_uint
+
+nodes_begin.argtypes = [POINTER(Nodes)]
+nodes_begin.restype = POINTER(Iter)
+
+nodes_get.argtypes = [POINTER(Nodes), POINTER(Iter)]
+nodes_get.restype = POINTER(Node)
+
+nodes_next.argtypes = [POINTER(Nodes), POINTER(Iter)]
+nodes_next.restype = POINTER(Iter)
+
+nodes_is_end.argtypes = [POINTER(Nodes), POINTER(Iter)]
+nodes_is_end.restype = c_bool
+
+nodes_get_first.argtypes = [POINTER(Nodes)]
+nodes_get_first.restype = POINTER(Node)
+
+nodes_contains.argtypes = [POINTER(Nodes), POINTER(Node)]
+nodes_contains.restype = c_bool
+
+nodes_merge.argtypes = [POINTER(Nodes), POINTER(Nodes)]
+nodes_merge.restype = POINTER(Nodes)
+
+plugins_size.argtypes = [POINTER(Plugins)]
+plugins_size.restype = c_uint
+
+plugins_begin.argtypes = [POINTER(Plugins)]
+plugins_begin.restype = POINTER(Iter)
+
+plugins_get.argtypes = [POINTER(Plugins), POINTER(Iter)]
+plugins_get.restype = POINTER(Plugin)
+
+plugins_next.argtypes = [POINTER(Plugins), POINTER(Iter)]
+plugins_next.restype = POINTER(Iter)
+
+plugins_is_end.argtypes = [POINTER(Plugins), POINTER(Iter)]
+plugins_is_end.restype = c_bool
+
+plugins_get_by_uri.argtypes = [POINTER(Plugins), POINTER(Node)]
+plugins_get_by_uri.restype = POINTER(Plugin)
+
+world_new.argtypes = []
+world_new.restype = POINTER(World)
+
+world_set_option.argtypes = [POINTER(World), String, POINTER(Node)]
+world_set_option.restype = None
+
+world_free.argtypes = [POINTER(World)]
+world_free.restype = None
+
+world_load_all.argtypes = [POINTER(World)]
+world_load_all.restype = None
+
+world_load_bundle.argtypes = [POINTER(World), POINTER(Node)]
+world_load_bundle.restype = None
+
+world_load_specifications.argtypes = [POINTER(World)]
+world_load_specifications.restype = None
+
+world_load_plugin_classes.argtypes = [POINTER(World)]
+world_load_plugin_classes.restype = None
+
+world_unload_bundle.argtypes = [POINTER(World), POINTER(Node)]
+world_unload_bundle.restype = c_int
+
+world_load_resource.argtypes = [POINTER(World), POINTER(Node)]
+world_load_resource.restype = c_int
+
+world_unload_resource.argtypes = [POINTER(World), POINTER(Node)]
+world_unload_resource.restype = c_int
+
+world_get_plugin_class.argtypes = [POINTER(World)]
+world_get_plugin_class.restype = POINTER(PluginClass)
+
+world_get_plugin_classes.argtypes = [POINTER(World)]
+world_get_plugin_classes.restype = POINTER(PluginClasses)
+
+world_get_all_plugins.argtypes = [POINTER(World)]
+world_get_all_plugins.restype = POINTER(Plugins)
+
+world_find_nodes.argtypes = [POINTER(World), POINTER(Node), POINTER(Node), POINTER(Node)]
+world_find_nodes.restype = POINTER(Nodes)
+
+world_get.argtypes = [POINTER(World), POINTER(Node), POINTER(Node), POINTER(Node)]
+world_get.restype = POINTER(Node)
+
+world_ask.argtypes = [POINTER(World), POINTER(Node), POINTER(Node), POINTER(Node)]
+world_ask.restype = c_bool
+
+plugin_verify.argtypes = [POINTER(Plugin)]
+plugin_verify.restype = c_bool
+
+plugin_get_uri.argtypes = [POINTER(Plugin)]
+plugin_get_uri.restype = POINTER(Node)
+
+plugin_get_bundle_uri.argtypes = [POINTER(Plugin)]
+plugin_get_bundle_uri.restype = POINTER(Node)
+
+plugin_get_data_uris.argtypes = [POINTER(Plugin)]
+plugin_get_data_uris.restype = POINTER(Nodes)
+
+plugin_get_library_uri.argtypes = [POINTER(Plugin)]
+plugin_get_library_uri.restype = POINTER(Node)
+
+plugin_get_name.argtypes = [POINTER(Plugin)]
+plugin_get_name.restype = POINTER(Node)
+
+plugin_get_class.argtypes = [POINTER(Plugin)]
+plugin_get_class.restype = POINTER(PluginClass)
+
+plugin_get_value.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_get_value.restype = POINTER(Nodes)
+
+plugin_has_feature.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_has_feature.restype = c_bool
+
+plugin_get_supported_features.argtypes = [POINTER(Plugin)]
+plugin_get_supported_features.restype = POINTER(Nodes)
+
+plugin_get_required_features.argtypes = [POINTER(Plugin)]
+plugin_get_required_features.restype = POINTER(Nodes)
+
+plugin_get_optional_features.argtypes = [POINTER(Plugin)]
+plugin_get_optional_features.restype = POINTER(Nodes)
+
+plugin_has_extension_data.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_has_extension_data.restype = c_bool
+
+plugin_get_extension_data.argtypes = [POINTER(Plugin)]
+plugin_get_extension_data.restype = POINTER(Nodes)
+
+plugin_get_num_ports.argtypes = [POINTER(Plugin)]
+plugin_get_num_ports.restype = c_uint32
+
+plugin_get_port_ranges_float.argtypes = [POINTER(Plugin), POINTER(c_float), POINTER(c_float), POINTER(c_float)]
+plugin_get_port_ranges_float.restype = None
+
+plugin_get_num_ports_of_class = VariadicFunction(_lib.lilv_plugin_get_num_ports_of_class,
+ c_uint32,
+ [POINTER(Plugin), POINTER(Node)])
+
+plugin_has_latency.argtypes = [POINTER(Plugin)]
+plugin_has_latency.restype = c_bool
+
+plugin_get_latency_port_index.argtypes = [POINTER(Plugin)]
+plugin_get_latency_port_index.restype = c_uint32
+
+plugin_get_port_by_index.argtypes = [POINTER(Plugin), c_uint32]
+plugin_get_port_by_index.restype = POINTER(Port)
+
+plugin_get_port_by_symbol.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_get_port_by_symbol.restype = POINTER(Port)
+
+plugin_get_port_by_designation.argtypes = [POINTER(Plugin), POINTER(Node), POINTER(Node)]
+plugin_get_port_by_designation.restype = POINTER(Port)
+
+plugin_get_project.argtypes = [POINTER(Plugin)]
+plugin_get_project.restype = POINTER(Node)
+
+plugin_get_author_name.argtypes = [POINTER(Plugin)]
+plugin_get_author_name.restype = POINTER(Node)
+
+plugin_get_author_email.argtypes = [POINTER(Plugin)]
+plugin_get_author_email.restype = POINTER(Node)
+
+plugin_get_author_homepage.argtypes = [POINTER(Plugin)]
+plugin_get_author_homepage.restype = POINTER(Node)
+
+plugin_is_replaced.argtypes = [POINTER(Plugin)]
+plugin_is_replaced.restype = c_bool
+
+plugin_get_related.argtypes = [POINTER(Plugin), POINTER(Node)]
+plugin_get_related.restype = POINTER(Nodes)
+
+port_get_node.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_node.restype = POINTER(Node)
+
+port_get_value.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_get_value.restype = POINTER(Nodes)
+
+port_get.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_get.restype = POINTER(Node)
+
+port_get_properties.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_properties.restype = POINTER(Nodes)
+
+port_has_property.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_has_property.restype = c_bool
+
+port_supports_event.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_supports_event.restype = c_bool
+
+port_get_index.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_index.restype = c_uint32
+
+port_get_symbol.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_symbol.restype = POINTER(Node)
+
+port_get_name.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_name.restype = POINTER(Node)
+
+port_get_classes.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_classes.restype = POINTER(Nodes)
+
+port_is_a.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(Node)]
+port_is_a.restype = c_bool
+
+port_get_range.argtypes = [POINTER(Plugin), POINTER(Port), POINTER(POINTER(Node)), POINTER(POINTER(Node)), POINTER(POINTER(Node))]
+port_get_range.restype = None
+
+port_get_scale_points.argtypes = [POINTER(Plugin), POINTER(Port)]
+port_get_scale_points.restype = POINTER(ScalePoints)
+
+state_new_from_world.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(Node)]
+state_new_from_world.restype = POINTER(State)
+
+state_new_from_file.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(Node), String]
+state_new_from_file.restype = POINTER(State)
+
+state_new_from_string.argtypes = [POINTER(World), POINTER(LV2_URID_Map), String]
+state_new_from_string.restype = POINTER(State)
+
+LilvGetPortValueFunc = CFUNCTYPE(c_void_p, c_char_p, POINTER(None), POINTER(c_uint32), POINTER(c_uint32))
+
+state_new_from_instance.argtypes = [POINTER(Plugin), POINTER(Instance), POINTER(LV2_URID_Map), c_char_p, c_char_p, c_char_p, String, LilvGetPortValueFunc, POINTER(None), c_uint32, POINTER(POINTER(LV2_Feature))]
+state_new_from_instance.restype = POINTER(State)
+
+state_free.argtypes = [POINTER(State)]
+state_free.restype = None
+
+state_equals.argtypes = [POINTER(State), POINTER(State)]
+state_equals.restype = c_bool
+
+state_get_num_properties.argtypes = [POINTER(State)]
+state_get_num_properties.restype = c_uint
+
+state_get_plugin_uri.argtypes = [POINTER(State)]
+state_get_plugin_uri.restype = POINTER(Node)
+
+state_get_uri.argtypes = [POINTER(State)]
+state_get_uri.restype = POINTER(Node)
+
+state_get_label.argtypes = [POINTER(State)]
+state_get_label.restype = c_char_p
+
+state_set_label.argtypes = [POINTER(State), String]
+state_set_label.restype = None
+
+state_set_metadata.argtypes = [POINTER(State), c_uint32, POINTER(None), c_size_t, c_uint32, c_uint32]
+state_set_metadata.restype = c_int
+
+LilvSetPortValueFunc = CFUNCTYPE(None, c_char_p, POINTER(None), POINTER(None), c_uint32, c_uint32)
+state_emit_port_values.argtypes = [POINTER(State), LilvSetPortValueFunc, POINTER(None)]
+state_emit_port_values.restype = None
+
+state_restore.argtypes = [POINTER(State), POINTER(Instance), LilvSetPortValueFunc, POINTER(None), c_uint32, POINTER(POINTER(LV2_Feature))]
+state_restore.restype = None
+
+state_save.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(LV2_URID_Unmap), POINTER(State), c_char_p, c_char_p, String]
+state_save.restype = c_int
+
+state_to_string.argtypes = [POINTER(World), POINTER(LV2_URID_Map), POINTER(LV2_URID_Unmap), POINTER(State), c_char_p, String]
+state_to_string.restype = c_char_p
+
+state_delete.argtypes = [POINTER(World), POINTER(State)]
+state_delete.restype = c_int
+
+scale_point_get_label.argtypes = [POINTER(ScalePoint)]
+scale_point_get_label.restype = POINTER(Node)
+
+scale_point_get_value.argtypes = [POINTER(ScalePoint)]
+scale_point_get_value.restype = POINTER(Node)
+
+plugin_class_get_parent_uri.argtypes = [POINTER(PluginClass)]
+plugin_class_get_parent_uri.restype = POINTER(Node)
+
+plugin_class_get_uri.argtypes = [POINTER(PluginClass)]
+plugin_class_get_uri.restype = POINTER(Node)
+
+plugin_class_get_label.argtypes = [POINTER(PluginClass)]
+plugin_class_get_label.restype = POINTER(Node)
+
+plugin_class_get_children.argtypes = [POINTER(PluginClass)]
+plugin_class_get_children.restype = POINTER(PluginClasses)
+
+plugin_instantiate.argtypes = [POINTER(Plugin), c_double, POINTER(POINTER(LV2_Feature))]
+plugin_instantiate.restype = POINTER(Instance)
+
+instance_free.argtypes = [POINTER(Instance)]
+instance_free.restype = None
+
+plugin_get_uis.argtypes = [POINTER(Plugin)]
+plugin_get_uis.restype = POINTER(UIs)
+
+ui_get_uri.argtypes = [POINTER(UI)]
+ui_get_uri.restype = POINTER(Node)
+
+ui_get_classes.argtypes = [POINTER(UI)]
+ui_get_classes.restype = POINTER(Nodes)
+
+ui_is_a.argtypes = [POINTER(UI), POINTER(Node)]
+ui_is_a.restype = c_bool
+
+LilvUISupportedFunc = CFUNCTYPE(c_uint, c_char_p, c_char_p)
+
+ui_is_supported.argtypes = [POINTER(UI), LilvUISupportedFunc, POINTER(Node), POINTER(POINTER(Node))]
+ui_is_supported.restype = c_uint
+
+ui_get_bundle_uri.argtypes = [POINTER(UI)]
+ui_get_bundle_uri.restype = POINTER(Node)
+
+ui_get_binary_uri.argtypes = [POINTER(UI)]
+ui_get_binary_uri.restype = POINTER(Node)
+
+OPTION_FILTER_LANG = 'http://drobilla.net/ns/lilv#filter-lang'
+OPTION_DYN_MANIFEST = 'http://drobilla.net/ns/lilv#dyn-manifest'
+
+# Define URI constants for compatibility with old Python bindings
+
+LILV_NS_DOAP = 'http://usefulinc.com/ns/doap#'
+LILV_NS_FOAF = 'http://xmlns.com/foaf/0.1/'
+LILV_NS_LILV = 'http://drobilla.net/ns/lilv#'
+LILV_NS_LV2 = 'http://lv2plug.in/ns/lv2core#'
+LILV_NS_OWL = 'http://www.w3.org/2002/07/owl#'
+LILV_NS_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
+LILV_NS_RDFS = 'http://www.w3.org/2000/01/rdf-schema#'
+LILV_NS_XSD = 'http://www.w3.org/2001/XMLSchema#'
+LILV_URI_ATOM_PORT = 'http://lv2plug.in/ns/ext/atom#AtomPort'
+LILV_URI_AUDIO_PORT = 'http://lv2plug.in/ns/lv2core#AudioPort'
+LILV_URI_CONTROL_PORT = 'http://lv2plug.in/ns/lv2core#ControlPort'
+LILV_URI_CV_PORT = 'http://lv2plug.in/ns/lv2core#CVPort'
+LILV_URI_EVENT_PORT = 'http://lv2plug.in/ns/ext/event#EventPort'
+LILV_URI_INPUT_PORT = 'http://lv2plug.in/ns/lv2core#InputPort'
+LILV_URI_MIDI_EVENT = 'http://lv2plug.in/ns/ext/midi#MidiEvent'
+LILV_URI_OUTPUT_PORT = 'http://lv2plug.in/ns/lv2core#OutputPort'
+LILV_URI_PORT = 'http://lv2plug.in/ns/lv2core#Port'
+LILV_OPTION_FILTER_LANG = 'http://drobilla.net/ns/lilv#filter-lang'
+LILV_OPTION_DYN_MANIFEST = 'http://drobilla.net/ns/lilv#dyn-manifest'
diff --git a/bindings/python/lv2_apply.py b/bindings/python/lv2_apply.py
new file mode 100755
index 0000000..4c7d9b4
--- /dev/null
+++ b/bindings/python/lv2_apply.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import math
+import lilv
+import sys
+import wave
+import numpy
+
+class WavFile(object):
+ """Helper class for accessing wav file data. Should work on the most common
+ formats (8 bit unsigned, 16 bit signed, 32 bit signed). Audio data is
+ converted to float32."""
+
+ # (struct format code, is_signedtype) for each sample width:
+ WAV_SPECS = {
+ 1: ("B", False),
+ 2: ("h", True),
+ 4: ("l", True),
+ }
+
+ def __init__(self, wav_in_path):
+ self.wav_in = wave.open(wav_in_path, 'r')
+ self.framerate = self.wav_in.getframerate()
+ self.nframes = self.wav_in.getnframes()
+ self.nchannels = self.wav_in.getnchannels()
+ self.sampwidth = self.wav_in.getsampwidth()
+ wav_spec = self.WAV_SPECS[self.sampwidth]
+ self.struct_fmt_code, self.signed = wav_spec
+ self.range = 2 ** (8*self.sampwidth)
+
+ def read(self):
+ """Read data from an open wav file. Return a list of channels, where each
+ channel is a list of floats."""
+ raw_bytes = self.wav_in.readframes(self.nframes)
+ struct_fmt = "%u%s" % (len(raw_bytes) / self.sampwidth, self.struct_fmt_code)
+ data = wave.struct.unpack(struct_fmt, raw_bytes)
+ if self.signed:
+ data = [i / float(self.range/2) for i in data]
+ else:
+ data = [(i - float(range/2)) / float(range/2) for i in data]
+
+ channels = []
+ for i in range(self.nchannels):
+ channels.append([data[j] for j in range(0, len(data), self.nchannels) ])
+
+ return channels
+
+ def close(self):
+ self.wav_in.close()
+
+def main():
+ # 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()
+ ns = world.ns
+ world.load_all()
+
+ plugin_uri = sys.argv[1]
+ wav_in_path = sys.argv[2]
+ wav_out_path = sys.argv[3]
+
+ # Find plugin
+ plugin_uri_node = world.new_uri(plugin_uri)
+ plugins = world.get_all_plugins()
+ if plugin_uri_node not in plugins:
+ print("Unknown plugin `%s'" % plugin_uri)
+ sys.exit(1)
+
+ plugin = plugins[plugin_uri_node]
+ n_audio_in = plugin.get_num_ports_of_class(ns.lv2.InputPort, ns.lv2.AudioPort)
+ n_audio_out = plugin.get_num_ports_of_class(ns.lv2.OutputPort, ns.lv2.AudioPort)
+ if n_audio_out == 0:
+ print("Plugin has no audio outputs\n")
+ sys.exit(1)
+
+ # Open input file
+ try:
+ wav_in = WavFile(wav_in_path)
+ except:
+ print("Failed to open input `%s'\n" % wav_in_path)
+ sys.exit(1)
+
+ if wav_in.nchannels != n_audio_in:
+ print("Input has %d channels, but plugin has %d audio inputs\n" % (
+ wav_in.nchannels, 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.wav_in.getparams())
+ wav_out.setnchannels(n_audio_out)
+
+ print('%s => %s => %s @ %d Hz'
+ % (wav_in_path, plugin.get_name(), wav_out_path, wav_in.framerate))
+
+ instance = lilv.Instance(plugin, wav_in.framerate)
+
+ channels = wav_in.read()
+ wav_in.close()
+
+ # Connect all ports to buffers. NB if we fail to connect any buffer, lilv
+ # will segfault.
+ audio_input_buffers = []
+ audio_output_buffers = []
+ control_input_buffers = []
+ control_output_buffers = []
+ for index in range(plugin.get_num_ports()):
+ port = plugin.get_port_by_index(index)
+ if port.is_a(ns.lv2.InputPort):
+ if port.is_a(ns.lv2.AudioPort):
+ audio_input_buffers.append(numpy.array(channels[len(audio_input_buffers)], numpy.float32))
+ instance.connect_port(index, audio_input_buffers[-1])
+ elif port.is_a(ns.lv2.ControlPort):
+ default = float(port.get(ns.lv2.default))
+ control_input_buffers.append(numpy.array([default], numpy.float32))
+ instance.connect_port(index, control_input_buffers[-1])
+ else:
+ raise ValueError("Unhandled port type")
+ elif port.is_a(ns.lv2.OutputPort):
+ if port.is_a(ns.lv2.AudioPort):
+ audio_output_buffers.append(numpy.array([0] * wav_in.nframes, numpy.float32))
+ instance.connect_port(index, audio_output_buffers[-1])
+ elif port.is_a(ns.lv2.ControlPort):
+ control_output_buffers.append(numpy.array([0], numpy.float32))
+ instance.connect_port(index, control_output_buffers[-1])
+ else:
+ raise ValueError("Unhandled port type")
+
+ # Run the plugin:
+ instance.run(wav_in.nframes)
+
+ # Interleave output buffers:
+ data = numpy.dstack(audio_output_buffers).flatten()
+
+ # Return to original int range:
+ if wav_in.signed:
+ data = data * float(wav_in.range / 2)
+ else:
+ data = (data + 1) * float(wav_in.range/2)
+
+ # Write output file in chunks to stop memory usage getting out of hand:
+ CHUNK_SIZE = 8192
+ for chunk in numpy.array_split(data, CHUNK_SIZE):
+ wav_out.writeframes(wave.struct.pack("%u%s" % (len(chunk), wav_in.struct_fmt_code), *chunk.astype(int)))
+ wav_out.close()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/bindings/python/lv2_list.py b/bindings/python/lv2_list.py
new file mode 100755
index 0000000..babe1b4
--- /dev/null
+++ b/bindings/python/lv2_list.py
@@ -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())
diff --git a/bindings/test/bindings_test_plugin.c b/bindings/test/bindings_test_plugin.c
new file mode 100644
index 0000000..3d6c763
--- /dev/null
+++ b/bindings/test/bindings_test_plugin.c
@@ -0,0 +1,196 @@
+/*
+ Copyright 2006-2011 David Robillard <d@drobilla.net>
+ Copyright 2006 Steve Harris <steve@plugin.org.uk>
+
+ 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 standard C headers */
+#include <math.h>
+#include <stdlib.h>
+
+/**
+ LV2 headers are based on the URI of the specification they come from, so a
+ consistent convention can be used even for unofficial extensions. The URI
+ of the core LV2 specification is <http://lv2plug.in/ns/lv2core>, by
+ replacing `http:/` with `lv2` any header in the specification bundle can be
+ included, in this case `lv2.h`.
+*/
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+/**
+ The URI is the identifier for a plugin, and how the host associates this
+ implementation in code with its description in data. In this plugin it is
+ only used once in the code, but defining the plugin URI at the top of the
+ file is a good convention to follow. If this URI does not match that used
+ in the data files, the host will fail to load the plugin.
+*/
+#define TEST_URI "http://example.org/lilv-bindings-test-plugin"
+
+/**
+ In code, ports are referred to by index. An enumeration of port indices
+ should be defined for readability.
+*/
+typedef enum {
+ TEST_CONTROL_IN = 0,
+ TEST_CONTROL_OUT = 1,
+ TEST_AUDIO_IN = 2,
+ TEST_AUDIO_OUT = 3
+} PortIndex;
+
+/**
+ Every plugin defines a private structure for the plugin instance. All data
+ associated with a plugin instance is stored here, and is available to
+ every instance method. In this simple plugin, only port buffers need to be
+ stored, since there is no additional instance data. */
+typedef struct {
+ float* buf;
+} Test;
+
+/**
+ The instantiate() function is called by the host to create a new plugin
+ instance. The host passes the plugin descriptor, sample rate, and bundle
+ path for plugins that need to load additional resources (e.g. waveforms).
+ The features parameter contains host-provided features defined in LV2
+ extensions, but this simple plugin does not use any.
+
+ This function is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* bundle_path,
+ const LV2_Feature* const* features)
+{
+ Test* test = (Test*)malloc(sizeof(Test));
+
+ return (LV2_Handle)test;
+}
+
+/**
+ The connect_port() method is called by the host to connect a particular port
+ to a buffer. The plugin must store the data location, but data may not be
+ accessed except in run().
+
+ This method is in the ``audio'' threading class, and is called in the same
+ context as run().
+*/
+static void
+connect_port(LV2_Handle instance,
+ uint32_t port,
+ void* data)
+{
+}
+
+/**
+ The activate() method is called by the host to initialise and prepare the
+ plugin instance for running. The plugin must reset all internal state
+ except for buffer locations set by connect_port(). Since this plugin has
+ no other internal state, this method does nothing.
+
+ This method is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static void
+activate(LV2_Handle instance)
+{
+}
+
+/** Process a block of audio (audio thread, must be RT safe). */
+static void
+run(LV2_Handle instance, uint32_t n_samples)
+{
+}
+
+/**
+ The deactivate() method is the counterpart to activate() called by the host
+ after running the plugin. It indicates that the host will not call run()
+ again until another call to activate() and is mainly useful for more
+ advanced plugins with ``live'' characteristics such as those with auxiliary
+ processing threads. As with activate(), this plugin has no use for this
+ information so this method does nothing.
+
+ This method is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static void
+deactivate(LV2_Handle instance)
+{
+}
+
+/**
+ Destroy a plugin instance (counterpart to instantiate()).
+
+ This method is in the ``instantiation'' threading class, so no other
+ methods on this instance will be called concurrently with it.
+*/
+static void
+cleanup(LV2_Handle instance)
+{
+ free(instance);
+}
+
+/**
+ The extension_data function returns any extension data supported by the
+ plugin. Note that this is not an instance method, but a function on the
+ plugin descriptor. It is usually used by plugins to implement additional
+ interfaces. This plugin does not have any extension data, so this function
+ returns NULL.
+
+ This method is in the ``discovery'' threading class, so no other functions
+ or methods in this plugin library will be called concurrently with it.
+*/
+static const void*
+extension_data(const char* uri)
+{
+ return NULL;
+}
+
+/**
+ Define the LV2_Descriptor for this plugin. It is best to define descriptors
+ statically to avoid leaking memory and non-portable shared library
+ constructors and destructors to clean up properly.
+*/
+static const LV2_Descriptor descriptor = {
+ TEST_URI,
+ instantiate,
+ connect_port,
+ activate,
+ run,
+ deactivate,
+ cleanup,
+ extension_data
+};
+
+/**
+ The lv2_descriptor() function is the entry point to the plugin library. The
+ host will load the library and call this function repeatedly with increasing
+ indices to find all the plugins defined in the library. The index is not an
+ indentifier, the URI of the returned descriptor is used to determine the
+ identify of the plugin.
+
+ This method is in the ``discovery'' threading class, so no other functions
+ or methods in this plugin library will be called concurrently with it.
+*/
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch (index) {
+ case 0:
+ return &descriptor;
+ default:
+ return NULL;
+ }
+}
diff --git a/bindings/test/bindings_test_plugin.ttl.in b/bindings/test/bindings_test_plugin.ttl.in
new file mode 100644
index 0000000..e8323d5
--- /dev/null
+++ b/bindings/test/bindings_test_plugin.ttl.in
@@ -0,0 +1,62 @@
+# Lilv Bindings 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 rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .
+
+<http://example.org/lilv-bindings-test-plugin>
+ a lv2:Plugin ;
+ doap:name "Lilv Bindings Test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ ui:ui <http://example.org/lilv-bindings-test-plugin-ui> ;
+ lv2:port [
+ a lv2:InputPort ,
+ lv2:ControlPort ;
+ lv2:index 0 ;
+ lv2:symbol "input" ;
+ lv2:name "Input" ;
+ lv2:default 0.5 ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 1.0 ;
+ lv2:scalePoint [ rdfs:label "off" ; rdf:value 0.0 ] ;
+ lv2:scalePoint [ rdfs:label "on" ; rdf:value 1.0 ] ;
+ ] , [
+ a lv2:OutputPort ,
+ lv2:ControlPort ;
+ lv2:index 1 ;
+ lv2:symbol "output" ;
+ lv2:name "Output"
+ ] , [
+ a lv2:AudioPort ,
+ lv2:InputPort ;
+ lv2:index 2 ;
+ lv2:symbol "audio_input" ;
+ lv2:name "Audio Input" ;
+ ] , [
+ a lv2:AudioPort ,
+ lv2:OutputPort ;
+ lv2:index 3 ;
+ lv2:symbol "audio_output" ;
+ lv2:name "Audio Output" ;
+ ] .
+
+<http://example.org/lilv-bindings-test-plugin-ui>
+ a ui:GtkUI ;
+ ui:binary <TODO> .
diff --git a/bindings/test/manifest.ttl.in b/bindings/test/manifest.ttl.in
new file mode 100644
index 0000000..9cc7fa8
--- /dev/null
+++ b/bindings/test/manifest.ttl.in
@@ -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-bindings-test-plugin>
+ a lv2:Plugin ;
+ lv2:binary <bindings_test_plugin@SHLIB_EXT@> ;
+ rdfs:seeAlso <bindings_test_plugin.ttl> .
diff --git a/bindings/test/python/test_api.py b/bindings/test/python/test_api.py
new file mode 100644
index 0000000..f594013
--- /dev/null
+++ b/bindings/test/python/test_api.py
@@ -0,0 +1,290 @@
+# Copyright 2016 David Robillard <d@drobilla.net>
+# Copyright 2013 Kaspar Emanuel <kaspar.emanuel@gmail.com>
+#
+# 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.
+
+import lilv
+import unittest
+import os
+
+location = "file://" + os.getcwd() + "/bindings/bindings_test_plugin.lv2/"
+
+class NodeTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ def testNodes(self):
+ aint = self.world.new_int(1)
+ aint2 = self.world.new_int(1)
+ aint3 = self.world.new_int(3)
+ afloat = self.world.new_float(2.0)
+ atrue = self.world.new_bool(True)
+ afalse = self.world.new_bool(False)
+ auri = self.world.new_uri("http://example.org")
+ afile = self.world.new_file_uri(None, "/foo/bar")
+ astring = self.world.new_string("hello")
+ self.assertEqual(auri.get_turtle_token(), '<http://example.org>')
+ self.assertTrue(aint.is_int())
+ self.assertTrue(afloat.is_float())
+ self.assertTrue(auri.is_uri())
+ self.assertTrue(astring.is_string())
+ self.assertTrue(astring.is_literal())
+ self.assertFalse(auri.is_blank())
+ self.assertTrue(int(aint) == 1)
+ self.assertTrue(float(afloat) == 2.0)
+ self.assertTrue(bool(atrue))
+ self.assertFalse(bool(afalse))
+ self.assertEqual(afile.get_path(), "/foo/bar")
+ self.assertTrue(aint == aint2)
+ self.assertTrue(aint != aint3)
+ self.assertTrue(aint != afloat)
+ with self.assertRaises(ValueError):
+ int(atrue)
+ with self.assertRaises(ValueError):
+ float(aint)
+ with self.assertRaises(ValueError):
+ bool(astring)
+
+class UriTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.load_all();
+ def testInvalidURI(self):
+ self.plugin_uri = self.world.new_uri("invalid_uri")
+ self.assertIsNone(self.plugin_uri)
+ def testNonExistentURI(self):
+ self.plugin_uri = self.world.new_uri("exist:does_not")
+ self.plugin = self.world.get_all_plugins().get_by_uri(self.plugin_uri)
+ self.assertEqual(self.plugin, None)
+ def testPortTypes(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_INPUT_PORT))
+ def testPortTypes2(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_OUTPUT_PORT))
+ def testPortTypes3(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_AUDIO_PORT))
+ def testPortTypes4(self):
+ self.assertIsNotNone(self.world.new_uri(lilv.LILV_URI_CONTROL_PORT))
+
+class PluginClassTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ def testPluginClasses(self):
+ pclass = self.world.get_plugin_class()
+ self.assertIsNotNone(pclass)
+ self.assertIsNone(pclass.get_parent_uri())
+ self.assertIsNotNone(pclass.get_uri())
+ self.assertIsNotNone(pclass.get_label())
+ self.assertEqual(str(pclass.get_uri()), str(pclass))
+ for i in pclass.get_children():
+ self.assertIsNotNone(i)
+ self.assertIsNotNone(i.get_uri())
+ self.assertIsNotNone(i.get_label())
+
+class PluginClassesTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.load_all()
+ def testPluginClasses(self):
+ classes = self.world.get_plugin_classes()
+ pclass = self.world.get_plugin_class()
+ self.assertIsNotNone(classes)
+ self.assertIsNotNone(pclass)
+ self.assertTrue(pclass in classes)
+ self.assertTrue(pclass.get_uri() in classes)
+ self.assertGreater(len(classes), 1)
+ self.assertIsNotNone(classes[0])
+ self.assertIsNotNone(classes[pclass.get_uri()])
+
+class LoadTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_specifications()
+ self.world.load_plugin_classes()
+ def tearDown(self):
+ del self.world
+ def testLoadUnload(self):
+ self.world.load_bundle(self.bundle_uri)
+ plugins = self.world.get_all_plugins()
+ plugin = plugins.get(plugins.begin())
+ self.world.load_resource(plugin)
+ self.world.unload_resource(plugin)
+ self.world.unload_bundle(self.bundle_uri)
+
+class PluginTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.set_option(lilv.OPTION_FILTER_LANG, self.world.new_bool(True))
+ self.bundle_uri = self.world.new_uri(location)
+ self.assertIsNotNone(self.bundle_uri, "Invalid URI: '" + location + "'")
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins.get(self.plugins.begin())
+ self.assertTrue(self.plugin.verify())
+ self.assertTrue(self.plugin in self.plugins)
+ self.assertTrue(self.plugin.get_uri() in self.plugins)
+ self.assertEqual(self.plugins[self.plugin.get_uri()], self.plugin)
+ self.assertIsNotNone(self.plugin, msg="Test plugin not found at location: '" + location + "'")
+ self.assertEqual(location, str(self.plugin.get_bundle_uri()))
+ self.plugin_uri = self.plugin.get_uri()
+ self.assertEqual(self.plugin.get_uri(), self.plugin_uri, "URI equality broken")
+ self.instance = lilv.Instance(self.plugin, 48000, None)
+ self.assertIsNotNone(self.instance)
+ self.lv2_InputPort = self.world.new_uri(lilv.LILV_URI_INPUT_PORT)
+ self.lv2_OutputPort = self.world.new_uri(lilv.LILV_URI_OUTPUT_PORT)
+ self.lv2_AudioPort = self.world.new_uri(lilv.LILV_URI_AUDIO_PORT)
+ self.lv2_ControlPort = self.world.new_uri(lilv.LILV_URI_CONTROL_PORT)
+ def testGetters(self):
+ self.assertIsNotNone(self.plugin.get_bundle_uri())
+ self.assertGreater(len(self.plugin.get_data_uris()), 0)
+ self.assertIsNotNone(self.plugin.get_library_uri())
+ self.assertTrue(self.plugin.get_name().is_string())
+ self.assertTrue(self.plugin.get_class().get_uri().is_uri())
+ self.assertEqual(len(self.plugin.get_value(self.world.ns.doap.license)), 1)
+ licenses = self.plugin.get_value(self.world.ns.doap.license)
+ features = self.plugin.get_value(self.world.ns.lv2.optionalFeature)
+ self.assertEqual(len(licenses), 1)
+ self.assertTrue(licenses[0] in licenses)
+ with self.assertRaises(IndexError):
+ self.assertIsNone(licenses[len(licenses)])
+ self.assertEqual(len(licenses) + len(features),
+ len(licenses.merge(features)))
+ self.assertEqual(licenses.get(licenses.begin()), self.world.new_uri('http://opensource.org/licenses/isc'))
+ self.assertEqual(licenses[0], licenses.get(licenses.begin()))
+ self.assertTrue(self.plugin.has_feature(self.world.ns.lv2.hardRTCapable))
+ self.assertEqual(len(self.plugin.get_supported_features()), 1)
+ self.assertEqual(len(self.plugin.get_optional_features()), 1)
+ self.assertEqual(len(self.plugin.get_required_features()), 0)
+ self.assertFalse(self.plugin.has_extension_data(self.world.new_uri('http://example.org/nope')))
+ self.assertEqual(len(self.plugin.get_extension_data()), 0)
+ self.assertEqual(len(self.plugin.get_extension_data()), 0)
+ self.assertFalse(self.plugin.has_latency())
+ self.assertIsNone(self.plugin.get_latency_port_index())
+ def testPorts(self):
+ self.assertEqual(self.plugin.get_num_ports(), 4)
+ self.assertIsNotNone(self.plugin.get_port(0))
+ self.assertIsNotNone(self.plugin.get_port(1))
+ self.assertIsNotNone(self.plugin.get_port(2))
+ self.assertIsNotNone(self.plugin.get_port(3))
+ self.assertIsNone(self.plugin.get_port_by_index(4))
+ self.assertIsNotNone(self.plugin.get_port("input"))
+ self.assertIsNotNone(self.plugin.get_port("output"))
+ self.assertIsNotNone(self.plugin.get_port("audio_input"))
+ self.assertIsNotNone(self.plugin.get_port("audio_output"))
+ self.assertIsNone(self.plugin.get_port_by_symbol("nonexistent"))
+ self.assertIsNone(self.plugin.get_port_by_designation(self.world.ns.lv2.InputPort, self.world.ns.lv2.control))
+ self.assertIsNone(self.plugin.get_project())
+ self.assertIsNone(self.plugin.get_author_name())
+ self.assertIsNone(self.plugin.get_author_email())
+ self.assertIsNone(self.plugin.get_author_homepage())
+ self.assertFalse(self.plugin.is_replaced())
+ self.assertEqual(0, len(self.plugin.get_related(self.world.new_uri("http://example.org/Type"))))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_AudioPort))
+ port = self.plugin.get_port("input")
+ self.assertTrue(port.get_node().is_blank())
+ self.assertEqual(0, port.get(self.world.ns.lv2.index))
+ self.assertEqual(1, len(port.get_value(self.world.ns.lv2.symbol)))
+ self.assertEqual(port.get_value(self.world.ns.lv2.symbol)[0], "input")
+ self.assertFalse(port.has_property(self.world.ns.lv2.latency))
+ self.assertFalse(port.supports_event(self.world.ns.midi.MidiEvent))
+ self.assertEqual(0, port.get_index())
+ self.assertEqual("input", port.get_symbol())
+ self.assertEqual("Input", port.get_name())
+ self.assertEqual([self.world.ns.lv2.ControlPort, self.world.ns.lv2.InputPort],
+ list(port.get_classes()))
+ self.assertTrue(port.is_a(self.world.ns.lv2.ControlPort))
+ self.assertFalse(port.is_a(self.world.ns.lv2.AudioPort))
+ self.assertEquals((0.5, 0.0, 1.0), port.get_range())
+ self.assertEquals(0, len(port.get_properties()))
+ def testScalePoints(self):
+ port = self.plugin.get_port("input")
+ points = port.get_scale_points()
+ self.assertEqual(points[0].get_label(), "off")
+ self.assertEqual(points[0].get_value(), 0.0)
+ self.assertEqual(points[1].get_label(), "on")
+ self.assertEqual(points[1].get_value(), 1.0)
+ def testPortCount(self):
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_OutputPort, self.lv2_AudioPort))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_OutputPort, self.lv2_ControlPort))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_AudioPort))
+ self.assertEqual(1, self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_ControlPort))
+
+class QueryTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.world.load_all()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins.get(self.plugins.begin())
+ def testNamespaces(self):
+ self.assertEqual(self.world.ns.lv2, "http://lv2plug.in/ns/lv2core#")
+ self.assertEqual(self.world.ns.lv2.Plugin, "http://lv2plug.in/ns/lv2core#Plugin")
+ def testQuery(self):
+ self.assertTrue(self.world.ask(None,
+ self.world.ns.rdf.type,
+ self.world.ns.lv2.Plugin))
+ self.assertLess(0, len(self.world.find_nodes(None,
+ self.world.ns.rdf.type,
+ self.world.ns.lv2.Plugin)))
+ self.assertEqual(self.plugin.get_uri(), self.world.get(None,
+ self.world.ns.rdf.type,
+ self.world.ns.lv2.Plugin))
+
+class InstanceTests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins[0]
+ self.instance = lilv.Instance(self.plugin, 48000)
+ self.assertEqual(self.plugin.get_uri(), self.instance.get_uri())
+ self.assertIsNone(self.instance.get_extension_data(self.world.new_uri("http://example.org/ext")))
+ self.assertIsNone(self.instance.get_extension_data("http://example.org/ext"))
+ def testRun(self):
+ import numpy
+ n_samples = 100
+ buf = numpy.zeros(n_samples)
+ with self.assertRaises(Exception):
+ self.instance.connect_port(0, "hello")
+ self.instance.connect_port(0, None)
+ self.instance.connect_port(0, None)
+ self.instance.connect_port(2, buf)
+ self.instance.connect_port(3, buf)
+ self.instance.activate()
+ self.instance.run(n_samples)
+ self.instance.deactivate()
+
+class UITests(unittest.TestCase):
+ def setUp(self):
+ self.world = lilv.World()
+ self.bundle_uri = self.world.new_uri(location)
+ self.world.load_bundle(self.bundle_uri)
+ self.plugins = self.world.get_all_plugins()
+ self.plugin = self.plugins[0]
+ def testUI(self):
+ uis = self.plugin.get_uis()
+ ui_uri = self.world.new_uri('http://example.org/lilv-bindings-test-plugin-ui')
+ self.assertEqual(1, len(uis))
+ self.assertEqual(str(uis[0]), str(ui_uri))
+ self.assertEqual(uis[0], str(ui_uri))
+ self.assertEqual(uis[0].get_uri(), ui_uri)
+ self.assertEqual(uis[0].get_bundle_uri(), self.bundle_uri)
+ self.assertEqual(uis[0].get_binary_uri(), str(self.bundle_uri) + "TODO")
+ self.assertEqual(uis[uis[0].get_uri()], uis[0])
+ self.assertTrue(uis[0].is_a(self.world.ns.ui.GtkUI))
+ self.assertTrue(uis[0] in uis)
+ self.assertTrue(uis[0].get_uri() in uis)
+ self.assertEqual([self.world.ns.ui.GtkUI], list(uis[0].get_classes()))
+ for ui in uis:
+ print(ui)
diff --git a/doc/layout.xml b/doc/layout.xml
new file mode 100644
index 0000000..74a109f
--- /dev/null
+++ b/doc/layout.xml
@@ -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>
diff --git a/doc/lv2apply.1 b/doc/lv2apply.1
new file mode 100644
index 0000000..4877aba
--- /dev/null
+++ b/doc/lv2apply.1
@@ -0,0 +1,34 @@
+.TH LV2APPLY 1 "05 Sep 2016"
+
+.SH NAME
+.B lv2apply \- apply an LV2 plugin to an audio file
+.SH SYNOPSIS
+.B lv2apply [OPTION]... PLUGIN_URI
+
+.SH OPTIONS
+.TP
+\fB\-i IN_FILE\fR
+Input file
+
+.TP
+\fB\-o OUT_FILE\fR
+Output file
+
+.TP
+\fB\-c SYM VAL\fR
+Set control port SYM to VAL
+
+.TP
+\fB\-\-help\fR
+Display help and exit
+
+.TP
+\fB\-\-version\fR
+Display version information and exit
+
+.SH "SEE ALSO"
+.BR lv2ls(1)
+.BR lv2info(1)
+
+.SH AUTHOR
+lv2apply was written by David Robillard <d@drobilla.net>
diff --git a/doc/lv2info.1 b/doc/lv2info.1
new file mode 100644
index 0000000..32a3bec
--- /dev/null
+++ b/doc/lv2info.1
@@ -0,0 +1,33 @@
+.TH LV2INFO 1 "05 Sep 2016"
+
+.SH NAME
+.B lv2info \- print information about an installed LV2 plugin
+.SH SYNOPSIS
+.B lv2info PLUGIN_URI
+
+.SH OPTIONS
+.TP
+\fB\-p FILE\fR
+Write Turtle description of plugin to FILE
+
+.TP
+\fB\-m FILE\fR
+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>
diff --git a/doc/lv2ls.1 b/doc/lv2ls.1
new file mode 100644
index 0000000..320b71c
--- /dev/null
+++ b/doc/lv2ls.1
@@ -0,0 +1,30 @@
+.TH LV2LS 1 "26 Aug 2016"
+
+.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>
diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in
new file mode 100644
index 0000000..e7a45d0
--- /dev/null
+++ b/doc/reference.doxygen.in
@@ -0,0 +1,2415 @@
+# Doxyfile 1.8.12
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = Lilv
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @LILV_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = .
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = NO
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 0.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = YES
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = YES
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = YES
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = YES
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = NO
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = NO
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = NO
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = NO
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE = @LILV_SRCDIR@/doc/layout.xml
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = YES
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = @LILV_SRCDIR@/lilv/lilv.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
+# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *.c
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = NO
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET = @LILV_SRCDIR@/doc/style.css
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 160
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 1
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME =
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = NO
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = NO
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = NO
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = NO
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = NO
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/doc/style.css b/doc/style.css
new file mode 100644
index 0000000..f6ff8bb
--- /dev/null
+++ b/doc/style.css
@@ -0,0 +1,691 @@
+body {
+ max-width: 80em;
+ margin: 0;
+ margin-left: auto;
+ margin-right: auto;
+ background: #FFF;
+ color: #000;
+}
+
+#titlearea {
+ display: none;
+}
+
+h1 {
+ font-size: 180%;
+ font-weight: 900;
+}
+
+h2 {
+ font-size: 140%;
+ font-weight: 700;
+}
+
+h3 {
+ font-size: 120%;
+ font-weight: 700;
+}
+
+h4 {
+ font-size: 110%;
+ font-weight: 700;
+}
+
+h5 {
+ font-size: 100%;
+ font-weight: 700;
+}
+
+h6 {
+ font-size: 100%;
+ font-weight: 600;
+}
+
+p {
+ margin: 0 0 1em 0;
+}
+
+dt {
+ font-weight: 700;
+}
+
+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: small;
+ text-align: center;
+}
+
+h3.version {
+ font-size: small;
+ text-align: center;
+}
+
+div.qindex,div.navtab {
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
+ text-align: center;
+ margin: 2px;
+ padding: 2px;
+}
+
+div.navtab {
+ margin-right: 15px;
+}
+
+/* @group Link Styling */
+a {
+ color: #546E00;
+ text-decoration: none;
+}
+
+.contents a:visited {
+ color: #344E00;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+a.qindexHL {
+ background-color: #9CAFD4;
+ color: #FFF;
+ border: 1px double #869DCA;
+}
+
+code {
+ color: #444;
+}
+
+a.code {
+ color: #4665A2;
+}
+
+a.codeRef {
+ color: #4665A2;
+}
+
+/* @end */
+dl.el {
+ margin-left: -1cm;
+}
+
+.fragment {
+ font-family: monospace, fixed;
+}
+
+pre.fragment {
+ border: 1px solid #C4C4C4;
+ background-color: #F9F9F9;
+ padding: 4px 6px;
+ margin: 4px 8px 4px 2px;
+ overflow: auto;
+ 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;
+}
+
+a + h2.groupheader {
+ display: none;
+}
+
+div.groupText {
+ margin-left: 16px;
+ font-style: italic;
+}
+
+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.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-right: 12px;
+}
+
+img.footer {
+ border: 0;
+ vertical-align: middle;
+}
+
+/* @group Code Colorization */
+span.keyword {
+ color: green;
+}
+
+span.keywordtype {
+ color: #3E873E;
+}
+
+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: x-small;
+}
+
+.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.125em;
+}
+
+h2.groupheader {
+ margin: 0.5em 0 0.25em 0;
+}
+
+.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams {
+ margin: 0;
+ padding: 0;
+}
+
+.mdescLeft,.mdescRight {
+ color: #555;
+}
+
+.memItemLeft,.memItemRight,.memTemplParams {
+ border: 0;
+ font-family: monospace, fixed;
+}
+
+.memItemLeft,.memTemplItemLeft {
+ white-space: nowrap;
+ padding-left: 2em;
+ padding-right: 1em;
+}
+
+.memItemLeft a.el {
+ font-weight: bold;
+}
+
+.memTemplParams {
+ color: #464646;
+ white-space: nowrap;
+}
+
+td.memSeparator {
+ display: none;
+}
+
+td.mlabels-right {
+ vertical-align: top;
+ padding-top: 4px;
+ color: #AA6;
+}
+
+.memtitle {
+ display: none;
+}
+
+/* @end */
+/* @group Member Details */
+/* Styles for detailed member documentation */
+.memtemplate {
+ 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: 1em 0 1em 0;
+}
+
+.memproto {
+ padding: 0;
+ font-weight: bold;
+ color: #000;
+}
+
+.memproto .paramname {
+ color: #444;
+ font-style: normal;
+}
+
+.memdoc {
+ padding: 0 0 0.5em 2em;
+}
+
+.paramkey {
+ text-align: right;
+}
+
+.paramtype {
+ color: #3E873E;
+ white-space: nowrap;
+}
+
+.paramname {
+ color: #444;
+ white-space: nowrap;
+ font-weight: bold;
+}
+
+td.paramname {
+ vertical-align: top;
+}
+
+.fieldname {
+ color: #000;
+}
+
+td.fieldname {
+ padding-right: 1em;
+ vertical-align: top;
+}
+
+td.fieldtype {
+ vertical-align: top;
+ color: #444;
+ padding-right: 0.5em;
+}
+
+td.fielddoc p {
+ margin: 0;
+}
+
+/* @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: small;
+ margin: 0.5em;
+}
+
+.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.5em;
+}
+
+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;
+}
+
+div.navpath {
+ padding: 0.25em;
+}
+
+.navpath ul {
+ font-size: x-small;
+ color: #8AA0CC;
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+}
+
+.navpath li {
+ list-style-type: none;
+ float: left;
+ padding-left: 10px;
+ padding-right: 15px;
+ color: #364D7C;
+}
+
+.navpath a {
+ display: block;
+ text-decoration: none;
+ outline: none;
+}
+
+.navpath a:hover {
+ color: #6884BD;
+}
+
+div.summary {
+ float: right;
+ font-size: x-small;
+ padding: 0.25em 0.5em 0 0;
+ width: 50%;
+ text-align: right;
+}
+
+div.summary a {
+ white-space: nowrap;
+}
+
+div.header {
+ background-color: #F3F3F3;
+ margin: 0;
+ border: 0;
+}
+
+div.headertitle {
+ font-size: 180%;
+ font-weight: bold;
+ color: #FFF;
+ padding: 0.125em 0.25em 0.125em 0.25em;
+ background-color: #333;
+ background: linear-gradient(to bottom, #333 0%, #111 100%);
+ border: solid 1px #444;
+ border-top: 0;
+ border-radius: 0 0 6px 6px;
+}
+
+div.line {
+ font-family: monospace, fixed;
+ font-size: 13px;
+ min-height: 13px;
+ line-height: 1.0;
+ text-wrap: avoid;
+ white-space: pre-wrap;
+ text-indent: -53px;
+ padding-left: 53px;
+ padding-bottom: 0;
+ margin: 0;
+}
+
+.glow {
+ background-color: cyan;
+ box-shadow: 0 0 10px cyan;
+}
+
+span.lineno {
+ padding-right: 4px;
+ text-align: right;
+ border-right: 2px solid #0F0;
+ background-color: #E8E8E8;
+ white-space: pre;
+}
+span.lineno a {
+ background-color: #D8D8D8;
+}
+
+span.lineno a:hover {
+ background-color: #C8C8C8;
+}
+
+.tabs, .tabs2, .navpath {
+ background-image: none;
+ background-color: #333;
+ background: linear-gradient(to bottom, #333 0%, #111 100%);
+ border: 0;
+ border-bottom: solid 2px #000;
+ padding: 0;
+ padding-top: 2px;
+ font-size: small;
+}
+
+#navrow1 {
+ border: 0;
+}
+
+th {
+ text-align: left;
+}
+
+.mlabel {
+ padding: 0.125em;
+}
+
+/* tabs*/
+
+.tablist {
+ margin: 0;
+ padding: 0;
+ display: table;
+}
+
+.tablist li {
+ display: table-cell;
+ line-height: 2em;
+ list-style: none;
+ background-color: #333;
+ background: linear-gradient(to bottom, #444 0%, #222 100%);
+ border: 1px solid #222;
+ border-bottom: 0;
+ border-radius: 6px 6px 0 0;
+ color: #DDD;
+}
+
+.tablist a {
+ display: block;
+ padding: 0 20px;
+ font-weight: bold;
+ color: #859900;
+ text-decoration: none;
+ outline: none;
+}
+
+.header a {
+ color: #859900;
+}
+
+.tabs3 .tablist a {
+ padding: 0 10px;
+}
+
+.tablist a:hover {
+ color: #fff;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 1.0);
+ text-decoration: none;
+}
+
+.tablist li.current a {
+ color: #fff;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 1.0);
+}
+
+span.icon {
+ display: none;
+}
diff --git a/lilv.pc.in b/lilv.pc.in
new file mode 100644
index 0000000..1f3b57d
--- /dev/null
+++ b/lilv.pc.in
@@ -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: lv2 @PKG_serd_0@ @PKG_sord_0@ @PKG_sratom_0@
+Libs: -L${libdir} -l@LIB_LILV@ @LILV_PKG_LIBS@
+Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@
diff --git a/lilv.ttl b/lilv.ttl
new file mode 100644
index 0000000..edfb0e5
--- /dev/null
+++ b/lilv.ttl
@@ -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" .
+
+
diff --git a/lilv/lilv.h b/lilv/lilv.h
new file mode 100644
index 0000000..f709cf1
--- /dev/null
+++ b/lilv/lilv.h
@@ -0,0 +1,1844 @@
+/*
+ Copyright 2007-2016 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 lilv.h API for Lilv, a lightweight LV2 host library.
+*/
+
+#ifndef LILV_LILV_H
+#define LILV_LILV_H
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+#ifdef LILV_SHARED
+# ifdef _WIN32
+# define LILV_LIB_IMPORT __declspec(dllimport)
+# define LILV_LIB_EXPORT __declspec(dllexport)
+# else
+# define LILV_LIB_IMPORT __attribute__((visibility("default")))
+# define LILV_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef LILV_INTERNAL
+# define LILV_API LILV_LIB_EXPORT
+# else
+# define LILV_API LILV_LIB_IMPORT
+# endif
+#else
+# define LILV_API
+#endif
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# define LILV_DEPRECATED __attribute__((__deprecated__))
+#else
+# define LILV_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+#define LILV_NS_DOAP "http://usefulinc.com/ns/doap#"
+#define LILV_NS_FOAF "http://xmlns.com/foaf/0.1/"
+#define LILV_NS_LILV "http://drobilla.net/ns/lilv#"
+#define LILV_NS_LV2 "http://lv2plug.in/ns/lv2core#"
+#define LILV_NS_OWL "http://www.w3.org/2002/07/owl#"
+#define LILV_NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define LILV_NS_RDFS "http://www.w3.org/2000/01/rdf-schema#"
+#define LILV_NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
+#define LILV_URI_ATOM_PORT "http://lv2plug.in/ns/ext/atom#AtomPort"
+#define LILV_URI_AUDIO_PORT "http://lv2plug.in/ns/lv2core#AudioPort"
+#define LILV_URI_CONTROL_PORT "http://lv2plug.in/ns/lv2core#ControlPort"
+#define LILV_URI_CV_PORT "http://lv2plug.in/ns/lv2core#CVPort"
+#define LILV_URI_EVENT_PORT "http://lv2plug.in/ns/ext/event#EventPort"
+#define LILV_URI_INPUT_PORT "http://lv2plug.in/ns/lv2core#InputPort"
+#define LILV_URI_MIDI_EVENT "http://lv2plug.in/ns/ext/midi#MidiEvent"
+#define LILV_URI_OUTPUT_PORT "http://lv2plug.in/ns/lv2core#OutputPort"
+#define LILV_URI_PORT "http://lv2plug.in/ns/lv2core#Port"
+
+typedef struct LilvPluginImpl LilvPlugin; /**< LV2 Plugin. */
+typedef struct LilvPluginClassImpl LilvPluginClass; /**< Plugin Class. */
+typedef struct LilvPortImpl LilvPort; /**< Port. */
+typedef struct LilvScalePointImpl LilvScalePoint; /**< Scale Point. */
+typedef struct LilvUIImpl LilvUI; /**< Plugin UI. */
+typedef struct LilvNodeImpl LilvNode; /**< Typed Value. */
+typedef struct LilvWorldImpl LilvWorld; /**< Lilv World. */
+typedef struct LilvInstanceImpl LilvInstance; /**< Plugin instance. */
+typedef struct LilvStateImpl LilvState; /**< Plugin state. */
+
+typedef void LilvIter; /**< Collection iterator */
+typedef void LilvPluginClasses; /**< set<PluginClass>. */
+typedef void LilvPlugins; /**< set<Plugin>. */
+typedef void LilvScalePoints; /**< set<ScalePoint>. */
+typedef void LilvUIs; /**< set<UI>. */
+typedef void LilvNodes; /**< set<Node>. */
+
+/**
+ @defgroup lilv Lilv
+ Lilv is a simple yet powerful C API for using LV2 plugins.
+
+ For more information about LV2, see <http://lv2plug.in>.
+ For more information about Lilv, see <http://drobilla.net/software/lilv>.
+ @{
+*/
+
+/**
+ Free memory allocated by Lilv.
+
+ This function exists because some systems require memory allocated by a
+ library to be freed by code in the same library. It is otherwise equivalent
+ to the standard C free() function.
+*/
+LILV_API void
+lilv_free(void* ptr);
+
+/**
+ @name Node
+ @{
+*/
+
+/**
+ Convert a file URI string to a local path string.
+ For example, "file://foo/bar/baz.ttl" returns "/foo/bar/baz.ttl".
+ Return value is shared and must not be deleted by caller.
+ This function does not handle escaping correctly and should not be used for
+ general file URIs. Use lilv_file_uri_parse() instead.
+ @return `uri` converted to a path, or NULL on failure (URI is not local).
+*/
+LILV_API LILV_DEPRECATED const char*
+lilv_uri_to_path(const char* uri);
+
+/**
+ Convert a file URI string to a local path string.
+ For example, "file://foo/bar%20one/baz.ttl" returns "/foo/bar one/baz.ttl".
+ Return value must be freed by caller with lilv_free().
+ @param uri The file URI to parse.
+ @param hostname If non-NULL, set to the hostname in the URI, if any.
+ @return `uri` converted to a path, or NULL on failure (URI is not local).
+*/
+LILV_API char*
+lilv_file_uri_parse(const char* uri, char** hostname);
+
+/**
+ Create a new URI value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_uri(LilvWorld* world, const char* uri);
+
+/**
+ Create a new file URI value.
+ @param world The world.
+ @param host Host name, or NULL.
+ @param path Path on host.
+ @return A new node that must be freed by caller.
+
+ Relative paths are resolved against the current working directory. Note
+ that this may yield unexpected results if `host` is another machine.
+*/
+LILV_API LilvNode*
+lilv_new_file_uri(LilvWorld* world, const char* host, const char* path);
+
+/**
+ Create a new string value (with no language).
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_string(LilvWorld* world, const char* str);
+
+/**
+ Create a new integer value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_int(LilvWorld* world, int val);
+
+/**
+ Create a new floating point value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_float(LilvWorld* world, float val);
+
+/**
+ Create a new boolean value.
+ Returned value must be freed by caller with lilv_node_free().
+*/
+LILV_API LilvNode*
+lilv_new_bool(LilvWorld* world, bool val);
+
+/**
+ Free a LilvNode.
+ It is safe to call this function on NULL.
+*/
+LILV_API void
+lilv_node_free(LilvNode* val);
+
+/**
+ Duplicate a LilvNode.
+*/
+LILV_API LilvNode*
+lilv_node_duplicate(const LilvNode* val);
+
+/**
+ Return whether two values are equivalent.
+*/
+LILV_API bool
+lilv_node_equals(const LilvNode* value, const LilvNode* other);
+
+/**
+ Return this value as a Turtle/SPARQL token.
+ Returned value must be freed by caller with lilv_free().
+ <table>
+ <caption>Example Turtle Tokens</caption>
+ <tr><th>URI</th><td>&lt;http://example.org/foo &gt;</td></tr>
+ <tr><th>QName</th><td>doap:name</td></tr>
+ <tr><th>String</th><td>"this is a string"</td></tr>
+ <tr><th>Float</th><td>1.0</td></tr>
+ <tr><th>Integer</th><td>1</td></tr>
+ <tr><th>Boolean</th><td>true</td></tr>
+ </table>
+*/
+LILV_API char*
+lilv_node_get_turtle_token(const LilvNode* value);
+
+/**
+ Return whether the value is a URI (resource).
+*/
+LILV_API bool
+lilv_node_is_uri(const LilvNode* value);
+
+/**
+ Return this value as a URI string, e.g. "http://example.org/foo".
+ Valid to call only if `lilv_node_is_uri(value)` returns true.
+ Returned value is owned by `value` and must not be freed by caller.
+*/
+LILV_API const char*
+lilv_node_as_uri(const LilvNode* value);
+
+/**
+ Return whether the value is a blank node (resource with no URI).
+*/
+LILV_API bool
+lilv_node_is_blank(const LilvNode* value);
+
+/**
+ Return this value as a blank node identifier, e.g. "genid03".
+ Valid to call only if `lilv_node_is_blank(value)` returns true.
+ Returned value is owned by `value` and must not be freed by caller.
+*/
+LILV_API const char*
+lilv_node_as_blank(const LilvNode* value);
+
+/**
+ Return whether this value is a literal (i.e. not a URI).
+ Returns true if `value` is a string or numeric value.
+*/
+LILV_API bool
+lilv_node_is_literal(const LilvNode* value);
+
+/**
+ Return whether this value is a string literal.
+ Returns true if `value` is a string value (and not numeric).
+*/
+LILV_API bool
+lilv_node_is_string(const LilvNode* value);
+
+/**
+ Return `value` as a string.
+*/
+LILV_API const char*
+lilv_node_as_string(const LilvNode* value);
+
+/**
+ Return the path of a file URI node.
+ Returns NULL if `value` is not a file URI.
+ Returned value must be freed by caller with lilv_free().
+*/
+LILV_API char*
+lilv_node_get_path(const LilvNode* value, char** hostname);
+
+/**
+ Return whether this value is a decimal literal.
+*/
+LILV_API bool
+lilv_node_is_float(const LilvNode* value);
+
+/**
+ Return `value` as a float.
+ Valid to call only if `lilv_node_is_float(value)` or
+ `lilv_node_is_int(value)` returns true.
+*/
+LILV_API float
+lilv_node_as_float(const LilvNode* value);
+
+/**
+ Return whether this value is an integer literal.
+*/
+LILV_API bool
+lilv_node_is_int(const LilvNode* value);
+
+/**
+ Return `value` as an integer.
+ Valid to call only if `lilv_node_is_int(value)` returns true.
+*/
+LILV_API int
+lilv_node_as_int(const LilvNode* value);
+
+/**
+ Return whether this value is a boolean.
+*/
+LILV_API bool
+lilv_node_is_bool(const LilvNode* value);
+
+/**
+ Return `value` as a bool.
+ Valid to call only if `lilv_node_is_bool(value)` returns true.
+*/
+LILV_API bool
+lilv_node_as_bool(const LilvNode* value);
+
+/**
+ @}
+ @name Collections
+ Lilv has several collection types for holding various types of value:
+ <ul>
+ <li>LilvPlugins (function prefix "lilv_plugins_")</li>
+ <li>LilvPluginClasses (function prefix "lilv_plugin_classes_")</li>
+ <li>LilvScalePoints (function prefix "lilv_scale_points_")</li>
+ <li>LilvNodes (function prefix "lilv_nodes_")</li>
+ <li>LilvUIs (function prefix "lilv_uis_")</li>
+ </ul>
+
+ Each collection type supports a similar basic API (except LilvPlugins which
+ is internal and thus lacks a free function):
+ <ul>
+ <li>void PREFIX_free (coll)</li>
+ <li>unsigned PREFIX_size (coll)</li>
+ <li>LilvIter* PREFIX_begin (coll)</li>
+ </ul>
+ @{
+*/
+
+/* Collections */
+
+/**
+ Iterate over each element of a collection.
+ @code
+ LILV_FOREACH(plugin_classes, i, classes) {
+ LilvPluginClass c = lilv_plugin_classes_get(classes, i);
+ // ...
+ }
+ @endcode
+*/
+#define LILV_FOREACH(colltype, iter, collection) \
+ for (LilvIter* iter = lilv_ ## colltype ## _begin(collection); \
+ !lilv_ ## colltype ## _is_end(collection, iter); \
+ (iter) = lilv_ ## colltype ## _next(collection, iter))
+
+/* LilvPluginClasses */
+
+LILV_API void
+lilv_plugin_classes_free(LilvPluginClasses* collection);
+
+LILV_API unsigned
+lilv_plugin_classes_size(const LilvPluginClasses* collection);
+
+LILV_API LilvIter*
+lilv_plugin_classes_begin(const LilvPluginClasses* collection);
+
+LILV_API const LilvPluginClass*
+lilv_plugin_classes_get(const LilvPluginClasses* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_plugin_classes_next(const LilvPluginClasses* collection, LilvIter* i);
+
+LILV_API bool
+lilv_plugin_classes_is_end(const LilvPluginClasses* collection, LilvIter* i);
+
+/**
+ Get a plugin class from `classes` by URI.
+ Return value is shared (stored in `classes`) and must not be freed or
+ modified by the caller in any way.
+ @return NULL if no plugin class with `uri` is found in `classes`.
+*/
+LILV_API const LilvPluginClass*
+lilv_plugin_classes_get_by_uri(const LilvPluginClasses* classes,
+ const LilvNode* uri);
+
+/* ScalePoints */
+
+LILV_API void
+lilv_scale_points_free(LilvScalePoints* collection);
+
+LILV_API unsigned
+lilv_scale_points_size(const LilvScalePoints* collection);
+
+LILV_API LilvIter*
+lilv_scale_points_begin(const LilvScalePoints* collection);
+
+LILV_API const LilvScalePoint*
+lilv_scale_points_get(const LilvScalePoints* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_scale_points_next(const LilvScalePoints* collection, LilvIter* i);
+
+LILV_API bool
+lilv_scale_points_is_end(const LilvScalePoints* collection, LilvIter* i);
+
+/* UIs */
+
+LILV_API void
+lilv_uis_free(LilvUIs* collection);
+
+LILV_API unsigned
+lilv_uis_size(const LilvUIs* collection);
+
+LILV_API LilvIter*
+lilv_uis_begin(const LilvUIs* collection);
+
+LILV_API const LilvUI*
+lilv_uis_get(const LilvUIs* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_uis_next(const LilvUIs* collection, LilvIter* i);
+
+LILV_API bool
+lilv_uis_is_end(const LilvUIs* collection, LilvIter* i);
+
+/**
+ Get a UI from `uis` by URI.
+ Return value is shared (stored in `uis`) and must not be freed or
+ modified by the caller in any way.
+ @return NULL if no UI with `uri` is found in `list`.
+*/
+LILV_API const LilvUI*
+lilv_uis_get_by_uri(const LilvUIs* uis,
+ const LilvNode* uri);
+
+/* Nodes */
+
+LILV_API void
+lilv_nodes_free(LilvNodes* collection);
+
+LILV_API unsigned
+lilv_nodes_size(const LilvNodes* collection);
+
+LILV_API LilvIter*
+lilv_nodes_begin(const LilvNodes* collection);
+
+LILV_API const LilvNode*
+lilv_nodes_get(const LilvNodes* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_nodes_next(const LilvNodes* collection, LilvIter* i);
+
+LILV_API bool
+lilv_nodes_is_end(const LilvNodes* collection, LilvIter* i);
+
+LILV_API LilvNode*
+lilv_nodes_get_first(const LilvNodes* collection);
+
+/**
+ Return whether `values` contains `value`.
+*/
+LILV_API bool
+lilv_nodes_contains(const LilvNodes* nodes, const LilvNode* value);
+
+/**
+ Return a new LilvNodes that contains all nodes from both `a` and `b`.
+*/
+LILV_API LilvNodes*
+lilv_nodes_merge(const LilvNodes* a, const LilvNodes* b);
+
+/* Plugins */
+
+LILV_API unsigned
+lilv_plugins_size(const LilvPlugins* collection);
+
+LILV_API LilvIter*
+lilv_plugins_begin(const LilvPlugins* collection);
+
+LILV_API const LilvPlugin*
+lilv_plugins_get(const LilvPlugins* collection, LilvIter* i);
+
+LILV_API LilvIter*
+lilv_plugins_next(const LilvPlugins* collection, LilvIter* i);
+
+LILV_API bool
+lilv_plugins_is_end(const LilvPlugins* collection, LilvIter* i);
+
+/**
+ Get a plugin from `plugins` by URI.
+ Return value is shared (stored in `plugins`) and must not be freed or
+ modified by the caller in any way.
+ @return NULL if no plugin with `uri` is found in `plugins`.
+*/
+LILV_API const LilvPlugin*
+lilv_plugins_get_by_uri(const LilvPlugins* plugins,
+ const LilvNode* uri);
+
+/**
+ @}
+ @name World
+ The "world" represents all Lilv state, and is used to discover/load/cache
+ LV2 data (plugins, UIs, and extensions).
+ Normal hosts which just need to load plugins by URI should simply use
+ lilv_world_load_all() to discover/load the system's LV2 resources.
+ @{
+*/
+
+/**
+ Initialize a new, empty world.
+ If initialization fails, NULL is returned.
+*/
+LILV_API LilvWorld*
+lilv_world_new(void);
+
+/**
+ Enable/disable language filtering.
+ Language filtering applies to any functions that return (a) value(s).
+ With filtering enabled, Lilv will automatically return the best value(s)
+ for the current LANG. With filtering disabled, all matching values will
+ be returned regardless of language tag. Filtering is enabled by default.
+*/
+#define LILV_OPTION_FILTER_LANG "http://drobilla.net/ns/lilv#filter-lang"
+
+/**
+ Enable/disable dynamic manifest support.
+ Dynamic manifest data will only be loaded if this option is true.
+*/
+#define LILV_OPTION_DYN_MANIFEST "http://drobilla.net/ns/lilv#dyn-manifest"
+
+/**
+ Set an option option for `world`.
+
+ Currently recognized options:
+ @ref LILV_OPTION_FILTER_LANG
+ @ref LILV_OPTION_DYN_MANIFEST
+*/
+LILV_API void
+lilv_world_set_option(LilvWorld* world,
+ const char* uri,
+ const LilvNode* value);
+
+/**
+ Destroy the world, mwahaha.
+ It is safe to call this function on NULL.
+ Note that destroying `world` will destroy all the objects it contains
+ (e.g. instances of LilvPlugin). Do not destroy the world until you are
+ finished with all objects that came from it.
+*/
+LILV_API void
+lilv_world_free(LilvWorld* world);
+
+/**
+ Load all installed LV2 bundles on the system.
+ This is the recommended way for hosts to load LV2 data. It implements the
+ established/standard best practice for discovering all LV2 data on the
+ system. The environment variable LV2_PATH may be used to control where
+ this function will look for bundles.
+
+ Hosts should use this function rather than explicitly load bundles, except
+ in special circumstances (e.g. development utilities, or hosts that ship
+ with special plugin bundles which are installed to a known location).
+*/
+LILV_API void
+lilv_world_load_all(LilvWorld* world);
+
+/**
+ Load a specific bundle.
+ `bundle_uri` must be a fully qualified URI to the bundle directory,
+ with the trailing slash, eg. file:///usr/lib/lv2/foo.lv2/
+
+ Normal hosts should not need this function (use lilv_world_load_all()).
+
+ Hosts MUST NOT attach any long-term significance to bundle paths
+ (e.g. in save files), since there are no guarantees they will remain
+ unchanged between (or even during) program invocations. Plugins (among
+ other things) MUST be identified by URIs (not paths) in save files.
+*/
+LILV_API void
+lilv_world_load_bundle(LilvWorld* world,
+ const LilvNode* bundle_uri);
+
+/**
+ Load all specifications from currently loaded bundles.
+
+ This is for hosts that explicitly load specific bundles, its use is not
+ necessary when using lilv_world_load_all(). This function parses the
+ specifications and adds them to the model.
+*/
+LILV_API void
+lilv_world_load_specifications(LilvWorld* world);
+
+/**
+ Load all plugin classes from currently loaded specifications.
+
+ Must be called after lilv_world_load_specifications(). This is for hosts
+ that explicitly load specific bundles, its use is not necessary when using
+ lilv_world_load_all().
+*/
+LILV_API void
+lilv_world_load_plugin_classes(LilvWorld* world);
+
+/**
+ Unload a specific bundle.
+
+ This unloads statements loaded by lilv_world_load_bundle(). Note that this
+ is not necessarily all information loaded from the bundle. If any resources
+ have been separately loaded with lilv_world_load_resource(), they must be
+ separately unloaded with lilv_world_unload_resource().
+*/
+LILV_API int
+lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri);
+
+/**
+ Load all the data associated with the given `resource`.
+ @param world The world.
+ @param resource Must be a subject (i.e. a URI or a blank node).
+ @return The number of files parsed, or -1 on error
+
+ All accessible data files linked to `resource` with rdfs:seeAlso will be
+ loaded into the world model.
+*/
+LILV_API int
+lilv_world_load_resource(LilvWorld* world,
+ const LilvNode* resource);
+
+/**
+ Unload all the data associated with the given `resource`.
+ @param world The world.
+ @param resource Must be a subject (i.e. a URI or a blank node).
+
+ This unloads all data loaded by a previous call to
+ lilv_world_load_resource() with the given `resource`.
+*/
+LILV_API int
+lilv_world_unload_resource(LilvWorld* world,
+ const LilvNode* resource);
+
+/**
+ Get the parent of all other plugin classes, lv2:Plugin.
+*/
+LILV_API const LilvPluginClass*
+lilv_world_get_plugin_class(const LilvWorld* world);
+
+/**
+ Return a list of all found plugin classes.
+ Returned list is owned by world and must not be freed by the caller.
+*/
+LILV_API const LilvPluginClasses*
+lilv_world_get_plugin_classes(const LilvWorld* world);
+
+/**
+ Return a list of all found plugins.
+ The returned list contains just enough references to query
+ or instantiate plugins. The data for a particular plugin will not be
+ loaded into memory until a call to an lilv_plugin_* function results in
+ a query (at which time the data is cached with the LilvPlugin so future
+ queries are very fast).
+
+ The returned list and the plugins it contains are owned by `world`
+ and must not be freed by caller.
+*/
+LILV_API const LilvPlugins*
+lilv_world_get_all_plugins(const LilvWorld* world);
+
+/**
+ Find nodes matching a triple pattern.
+ Either `subject` or `object` may be NULL (i.e. a wildcard), but not both.
+ @return All matches for the wildcard field, or NULL.
+*/
+LILV_API LilvNodes*
+lilv_world_find_nodes(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object);
+
+/**
+ Find a single node that matches a pattern.
+ Exactly one of `subject`, `predicate`, `object` must be NULL.
+ This function is equivalent to
+ lilv_nodes_get_first(lilv_world_find_nodes(...)) but simplifies the common
+ case of only wanting a single value.
+ @return the first matching node, or NULL if no matches are found.
+*/
+LILV_API LilvNode*
+lilv_world_get(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object);
+
+/**
+ Return true iff a statement matching a certain pattern exists.
+
+ This is useful for checking if particular statement exists without having to
+ bother with collections and memory management.
+
+ @param world The world.
+ @param subject Subject of statement, or NULL for anything.
+ @param predicate Predicate (key) of statement, or NULL for anything.
+ @param object Object (value) of statement, or NULL for anything.
+*/
+LILV_API bool
+lilv_world_ask(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object);
+
+/**
+ Get an LV2 symbol for some subject.
+
+ This will return the lv2:symbol property of the subject if it is given
+ explicitly, and otherwise will attempt to derive a symbol from the URI.
+ @return A string node that is a valid LV2 symbol, or NULL on error.
+*/
+LILV_API LilvNode*
+lilv_world_get_symbol(LilvWorld* world, const LilvNode* subject);
+
+/**
+ @}
+ @name Plugin
+ @{
+*/
+
+/**
+ Check if `plugin` is valid.
+ This is not a rigorous validator, but can be used to reject some malformed
+ plugins that could cause bugs (e.g. plugins with missing required fields).
+
+ Note that normal hosts do NOT need to use this - lilv does not
+ load invalid plugins into plugin lists. This is included for plugin
+ testing utilities, etc.
+ @return true iff `plugin` is valid.
+*/
+LILV_API bool
+lilv_plugin_verify(const LilvPlugin* plugin);
+
+/**
+ Get the URI of `plugin`.
+ Any serialization that refers to plugins should refer to them by this.
+ Hosts SHOULD NOT save any filesystem paths, plugin indexes, etc. in saved
+ files; save only the URI.
+
+ The URI is a globally unique identifier for one specific plugin. Two
+ plugins with the same URI are compatible in port signature, and should
+ be guaranteed to work in a compatible and consistent way. If a plugin
+ is upgraded in an incompatible way (eg if it has different ports), it
+ MUST have a different URI than it's predecessor.
+
+ @return A shared URI value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_plugin_get_uri(const LilvPlugin* plugin);
+
+/**
+ Get the (resolvable) URI of the plugin's "main" bundle.
+ This returns the URI of the bundle where the plugin itself was found. Note
+ that the data for a plugin may be spread over many bundles, that is,
+ lilv_plugin_get_data_uris() may return URIs which are not within this
+ bundle.
+
+ Typical hosts should not need to use this function.
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv_file_uri_parse().
+ @return a shared string which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_plugin_get_bundle_uri(const LilvPlugin* plugin);
+
+/**
+ Get the (resolvable) URIs of the RDF data files that define a plugin.
+ Typical hosts should not need to use this function.
+ Note this always returns fully qualified URIs. If you want local
+ filesystem paths, use lilv_file_uri_parse().
+ @return a list of complete URLs eg. "file:///foo/ABundle.lv2/aplug.ttl",
+ which is shared and must not be modified or freed.
+*/
+LILV_API const LilvNodes*
+lilv_plugin_get_data_uris(const LilvPlugin* plugin);
+
+/**
+ Get the (resolvable) URI of the shared library for `plugin`.
+ Note this always returns a fully qualified URI. If you want a local
+ filesystem path, use lilv_file_uri_parse().
+ @return a shared string which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_plugin_get_library_uri(const LilvPlugin* plugin);
+
+/**
+ Get the name of `plugin`.
+ This returns the name (doap:name) of the plugin. The name may be
+ translated according to the current locale, this value MUST NOT be used
+ as a plugin identifier (use the URI for that).
+ Returned value must be freed by the caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_name(const LilvPlugin* plugin);
+
+/**
+ Get the class this plugin belongs to (e.g. Filters).
+*/
+LILV_API const LilvPluginClass*
+lilv_plugin_get_class(const LilvPlugin* plugin);
+
+/**
+ Get a value associated with the plugin in a plugin's data files.
+ `predicate` must be either a URI or a QName.
+
+ Returns the ?object of all triples found of the form:
+
+ <code>&lt;plugin-uri&gt; predicate ?object</code>
+
+ May return NULL if the property was not found, or if object(s) is not
+ sensibly represented as a LilvNodes (e.g. blank nodes).
+ Return value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_value(const LilvPlugin* plugin,
+ const LilvNode* predicate);
+
+/**
+ Return whether a feature is supported by a plugin.
+ This will return true if the feature is an optional or required feature
+ of the plugin.
+*/
+LILV_API bool
+lilv_plugin_has_feature(const LilvPlugin* plugin,
+ const LilvNode* feature);
+
+/**
+ Get the LV2 Features supported (required or optionally) by a plugin.
+ A feature is "supported" by a plugin if it is required OR optional.
+
+ Since required features have special rules the host must obey, this function
+ probably shouldn't be used by normal hosts. Using lilv_plugin_get_optional_features()
+ and lilv_plugin_get_required_features() separately is best in most cases.
+
+ Returned value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_supported_features(const LilvPlugin* plugin);
+
+/**
+ Get the LV2 Features required by a plugin.
+ If a feature is required by a plugin, hosts MUST NOT use the plugin if they do not
+ understand (or are unable to support) that feature.
+
+ All values returned here MUST be passed to the plugin's instantiate method
+ (along with data, if necessary, as defined by the feature specification)
+ or plugin instantiation will fail.
+
+ Return value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_required_features(const LilvPlugin* plugin);
+
+/**
+ Get the LV2 Features optionally supported by a plugin.
+ Hosts MAY ignore optional plugin features for whatever reasons. Plugins
+ MUST operate (at least somewhat) if they are instantiated without being
+ passed optional features.
+
+ Return value must be freed by caller with lilv_nodes_free().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_optional_features(const LilvPlugin* plugin);
+
+/**
+ Return whether or not a plugin provides a specific extension data.
+*/
+LILV_API bool
+lilv_plugin_has_extension_data(const LilvPlugin* plugin,
+ const LilvNode* uri);
+
+/**
+ Get a sequence of all extension data provided by a plugin.
+ This can be used to find which URIs lilv_instance_get_extension_data()
+ will return a value for without instantiating the plugin.
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_extension_data(const LilvPlugin* plugin);
+
+/**
+ Get the number of ports on this plugin.
+*/
+LILV_API uint32_t
+lilv_plugin_get_num_ports(const LilvPlugin* plugin);
+
+/**
+ Get the port ranges (minimum, maximum and default values) for all ports.
+ `min_values`, `max_values` and `def_values` must either point to an array
+ of N floats, where N is the value returned by lilv_plugin_get_num_ports()
+ for this plugin, or NULL. The elements of the array will be set to the
+ the minimum, maximum and default values of the ports on this plugin,
+ with array index corresponding to port index. If a port doesn't have a
+ minimum, maximum or default value, or the port's type is not float, the
+ corresponding array element will be set to NAN.
+
+ This is a convenience method for the common case of getting the range of
+ all float ports on a plugin, and may be significantly faster than
+ repeated calls to lilv_port_get_range().
+*/
+LILV_API void
+lilv_plugin_get_port_ranges_float(const LilvPlugin* plugin,
+ float* min_values,
+ float* max_values,
+ float* def_values);
+
+/**
+ Get the number of ports on this plugin that are members of some class(es).
+ Note that this is a varargs function so ports fitting any type 'profile'
+ desired can be found quickly. REMEMBER TO TERMINATE THE PARAMETER LIST
+ OF THIS FUNCTION WITH NULL OR VERY NASTY THINGS WILL HAPPEN.
+*/
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class(const LilvPlugin* plugin,
+ const LilvNode* class_1, ...);
+
+#ifndef SWIG
+/**
+ Variant of lilv_plugin_get_num_ports_of_class() that takes a va_list.
+
+ This function calls va_arg() on `args` but does not call va_end().
+*/
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class_va(const LilvPlugin* plugin,
+ const LilvNode* class_1,
+ va_list args);
+#endif
+
+/**
+ Return whether or not the plugin introduces (and reports) latency.
+ The index of the latency port can be found with
+ lilv_plugin_get_latency_port() ONLY if this function returns true.
+*/
+LILV_API bool
+lilv_plugin_has_latency(const LilvPlugin* plugin);
+
+/**
+ Return the index of the plugin's latency port.
+ It is a fatal error to call this on a plugin without checking if the port
+ exists by first calling lilv_plugin_has_latency().
+
+ Any plugin that introduces unwanted latency that should be compensated for
+ (by hosts with the ability/need) MUST provide this port, which is a control
+ rate output port that reports the latency for each cycle in frames.
+*/
+LILV_API uint32_t
+lilv_plugin_get_latency_port_index(const LilvPlugin* plugin);
+
+/**
+ Get a port on `plugin` by `index`.
+*/
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_index(const LilvPlugin* plugin,
+ uint32_t index);
+
+/**
+ Get a port on `plugin` by `symbol`.
+ Note this function is slower than lilv_plugin_get_port_by_index(),
+ especially on plugins with a very large number of ports.
+*/
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_symbol(const LilvPlugin* plugin,
+ const LilvNode* symbol);
+
+/**
+ Get a port on `plugin` by its lv2:designation.
+
+ The designation of a port describes the meaning, assignment, allocation or
+ role of the port, e.g. "left channel" or "gain". If found, the port with
+ matching `port_class` and `designation` is be returned, otherwise NULL is
+ returned. The `port_class` can be used to distinguish the input and output
+ ports for a particular designation. If `port_class` is NULL, any port with
+ the given designation will be returned.
+*/
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_designation(const LilvPlugin* plugin,
+ const LilvNode* port_class,
+ const LilvNode* designation);
+
+/**
+ Get the project the plugin is a part of.
+
+ More information about the project can be read via lilv_world_find_nodes(),
+ typically using properties from DOAP (e.g. doap:name).
+*/
+LILV_API LilvNode*
+lilv_plugin_get_project(const LilvPlugin* plugin);
+
+/**
+ Get the full name of the plugin's author.
+ Returns NULL if author name is not present.
+ Returned value must be freed by caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_author_name(const LilvPlugin* plugin);
+
+/**
+ Get the email address of the plugin's author.
+ Returns NULL if author email address is not present.
+ Returned value must be freed by caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_author_email(const LilvPlugin* plugin);
+
+/**
+ Get the address of the plugin author's home page.
+ Returns NULL if author homepage is not present.
+ Returned value must be freed by caller.
+*/
+LILV_API LilvNode*
+lilv_plugin_get_author_homepage(const LilvPlugin* plugin);
+
+/**
+ Return true iff `plugin` has been replaced by another plugin.
+
+ The plugin will still be usable, but hosts should hide them from their
+ user interfaces to prevent users from using deprecated plugins.
+*/
+LILV_API bool
+lilv_plugin_is_replaced(const LilvPlugin* plugin);
+
+/**
+ Write the Turtle description of `plugin` to `plugin_file`.
+
+ This function is particularly useful for porting plugins in conjunction with
+ an LV2 bridge such as NASPRO.
+*/
+LILV_API void
+lilv_plugin_write_description(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* plugin_file);
+
+/**
+ Write a manifest entry for `plugin` to `manifest_file`.
+
+ This function is intended for use with lilv_plugin_write_description() to
+ write a complete description of a plugin to a bundle.
+*/
+LILV_API void
+lilv_plugin_write_manifest_entry(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* manifest_file,
+ const char* plugin_file_path);
+
+/**
+ Get the resources related to `plugin` with lv2:appliesTo.
+
+ Some plugin-related resources are not linked directly to the plugin with
+ rdfs:seeAlso and thus will not be automatically loaded along with the plugin
+ data (usually for performance reasons). All such resources of the given @c
+ type related to `plugin` can be accessed with this function.
+
+ If `type` is NULL, all such resources will be returned, regardless of type.
+
+ To actually load the data for each returned resource, use
+ lilv_world_load_resource().
+*/
+LILV_API LilvNodes*
+lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type);
+
+/**
+ @}
+ @name Port
+ @{
+*/
+
+/**
+ Get the RDF node of `port`.
+
+ Ports nodes may be may be URIs or blank nodes.
+
+ @return A shared node which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_port_get_node(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Port analog of lilv_plugin_get_value().
+*/
+LILV_API LilvNodes*
+lilv_port_get_value(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate);
+
+/**
+ Get a single property value of a port.
+
+ This is equivalent to lilv_nodes_get_first(lilv_port_get_value(...)) but is
+ simpler to use in the common case of only caring about one value. The
+ caller is responsible for freeing the returned node.
+*/
+LILV_API LilvNode*
+lilv_port_get(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate);
+
+/**
+ Return the LV2 port properties of a port.
+*/
+LILV_API LilvNodes*
+lilv_port_get_properties(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Return whether a port has a certain property.
+*/
+LILV_API bool
+lilv_port_has_property(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* property);
+
+/**
+ Return whether a port supports a certain event type.
+
+ More precisely, this returns true iff the port has an atom:supports or an
+ ev:supportsEvent property with `event_type` as the value.
+*/
+LILV_API bool
+lilv_port_supports_event(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* event_type);
+
+/**
+ Get the index of a port.
+ The index is only valid for the life of the plugin and may change between
+ versions. For a stable identifier, use the symbol.
+*/
+LILV_API uint32_t
+lilv_port_get_index(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Get the symbol of a port.
+ The 'symbol' is a short string, a valid C identifier.
+ Returned value is owned by `port` and must not be freed.
+*/
+LILV_API const LilvNode*
+lilv_port_get_symbol(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Get the name of a port.
+ This is guaranteed to return the untranslated name (the doap:name in the
+ data file without a language tag). Returned value must be freed by
+ the caller.
+*/
+LILV_API LilvNode*
+lilv_port_get_name(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Get all the classes of a port.
+ This can be used to determine if a port is an input, output, audio,
+ control, midi, etc, etc, though it's simpler to use lilv_port_is_a().
+ The returned list does not include lv2:Port, which is implied.
+ Returned value is shared and must not be destroyed by caller.
+*/
+LILV_API const LilvNodes*
+lilv_port_get_classes(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ Determine if a port is of a given class (input, output, audio, etc).
+ For convenience/performance/extensibility reasons, hosts are expected to
+ create a LilvNode for each port class they "care about". Well-known type
+ URI strings are defined (e.g. LILV_URI_INPUT_PORT) for convenience, but
+ this function is designed so that Lilv is usable with any port types
+ without requiring explicit support in Lilv.
+*/
+LILV_API bool
+lilv_port_is_a(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* port_class);
+
+/**
+ Get the default, minimum, and maximum values of a port.
+
+ `def`, `min`, and `max` are outputs, pass pointers to uninitialized
+ LilvNode* variables. These will be set to point at new values (which must
+ be freed by the caller using lilv_node_free()), or NULL if the value does
+ not exist.
+*/
+LILV_API void
+lilv_port_get_range(const LilvPlugin* plugin,
+ const LilvPort* port,
+ LilvNode** def,
+ LilvNode** min,
+ LilvNode** max);
+
+/**
+ Get the scale points (enumeration values) of a port.
+ This returns a collection of 'interesting' named values of a port
+ (e.g. appropriate entries for a UI selector associated with this port).
+ Returned value may be NULL if `port` has no scale points, otherwise it
+ must be freed by caller with lilv_scale_points_free().
+*/
+LILV_API LilvScalePoints*
+lilv_port_get_scale_points(const LilvPlugin* plugin,
+ const LilvPort* port);
+
+/**
+ @}
+ @name Plugin State
+ @{
+*/
+
+/**
+ Load a state snapshot from the world RDF model.
+ This function can be used to load the default state of a plugin by passing
+ the plugin URI as the `subject` parameter.
+ @param world The world.
+ @param map URID mapper.
+ @param node The subject of the state description (e.g. a preset URI).
+ @return A new LilvState which must be freed with lilv_state_free(), or NULL.
+*/
+LILV_API LilvState*
+lilv_state_new_from_world(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* node);
+
+/**
+ Load a state snapshot from a file.
+ @param world The world.
+ @param map URID mapper.
+ @param subject The subject of the state description (e.g. a preset URI).
+ @param path The path of the file containing the state description.
+ @return A new LilvState which must be freed with lilv_state_free().
+
+ If `subject` is NULL, it is taken to be the URI of the file (i.e.
+ "<>" in Turtle).
+
+ This function parses the file separately to create the state, it does not
+ parse the file into the world model, i.e. the returned state is the only
+ new memory consumed once this function returns.
+*/
+LILV_API LilvState*
+lilv_state_new_from_file(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* subject,
+ const char* path);
+
+/**
+ Load a state snapshot from a string made by lilv_state_to_string().
+*/
+LILV_API LilvState*
+lilv_state_new_from_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ const char* str);
+
+/**
+ Function to get a port value.
+ @param port_symbol The symbol of the port.
+ @param user_data The user_data passed to lilv_state_new_from_instance().
+ @param size (Output) The size of the returned value.
+ @param type (Output) The URID of the type of the returned value.
+ @return A pointer to the port value.
+
+ This function MUST set `size` and `type` appropriately.
+*/
+typedef const void* (*LilvGetPortValueFunc)(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type);
+
+/**
+ Create a new state snapshot from a plugin instance.
+
+ @param plugin The plugin this state applies to.
+
+ @param instance An instance of `plugin`.
+
+ @param map The map to use for mapping URIs in state.
+
+ @param file_dir Directory of files created by the plugin earlier (or NULL).
+ This is for hosts that support file creation at any time with state
+ state:makePath. These files will be copied as necessary to `copy_dir` and
+ not be referred to directly in state (a temporary directory is appropriate).
+
+ @param copy_dir Directory of copies of files in `file_dir` (or NULL). This
+ directory will have the same structure as `file_dir` but with possibly
+ modified file names to distinguish different revisions. If you only care
+ about saving one state snapshot, it can be the same as `save_dir`. Plugin
+ state will refer to files in this directory.
+
+ @param save_dir Directory of files created by plugin during save (or NULL).
+ If the state will be saved, this should be the bundle directory later passed
+ to lilv_state_save().
+
+ @param get_value Function to get port values (or NULL). If NULL, the
+ returned state will not represent port values. This should only be NULL in
+ hosts that save and restore port values via some other mechanism.
+
+ @param user_data User data to pass to `get_value`.
+
+ @param link_dir Directory of links to external files (or NULL). A link will
+ be made in this directory to any external files referred to in plugin state.
+ In turn, links will be created in the save directory to these links (e.g.
+ save_dir/file => link_dir/file => /foo/bar/file). This allows many state
+ snapshots to share a single link to an external file, so archival
+ (e.g. with tar -h) will not create several copies of the file. If this is
+ not required, it can be the same as save_dir.
+
+ @param flags Bitwise OR of LV2_State_Flags values.
+
+ @param features Features to pass LV2_State_Interface.save().
+
+ @return A new LilvState which must be freed with lilv_state_free().
+
+ This function may be called simultaneously with any instance function
+ (except discovery functions) unless the threading class of that function
+ explicitly disallows this.
+
+ To support advanced file functionality, there are several directory
+ parameters. Simple hosts that only wish to save a single plugins state once
+ may simply use the same directory for all of them (or pass NULL to not
+ support files at all). The multiple parameters are necessary to support
+ saving an instances state many times while avoiding any duplication of data.
+
+ If supported (via state:makePath passed to LV2_Descriptor::instantiate()),
+ `file_dir` should be the directory where any files created by the plugin
+ (not during save time, e.g. during instantiation) are stored. These files
+ will be copied to preserve their state at this time.plugin-created files are stored.
+ Lilv will assume any files within this directory (recursively) are created
+ by the plugin and all other files are immutable. Note that this function
+ does not save the state, use lilv_state_save() for that.
+
+ See <a href="http://lv2plug.in/ns/ext/state/state.h">state.h</a> from the
+ LV2 State extension for details on the `flags` and `features` parameters.
+*/
+LILV_API LilvState*
+lilv_state_new_from_instance(const LilvPlugin* plugin,
+ LilvInstance* instance,
+ LV2_URID_Map* map,
+ const char* file_dir,
+ const char* copy_dir,
+ const char* link_dir,
+ const char* save_dir,
+ LilvGetPortValueFunc get_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features);
+
+/**
+ Free `state`.
+*/
+LILV_API void
+lilv_state_free(LilvState* state);
+
+/**
+ Return true iff `a` is equivalent to `b`.
+*/
+LILV_API bool
+lilv_state_equals(const LilvState* a, const LilvState* b);
+
+/**
+ Return the number of properties in `state`.
+*/
+LILV_API unsigned
+lilv_state_get_num_properties(const LilvState* state);
+
+/**
+ Get the URI of the plugin `state` applies to.
+*/
+LILV_API const LilvNode*
+lilv_state_get_plugin_uri(const LilvState* state);
+
+/**
+ Get the URI of `state`.
+
+ This may return NULL if the state has not been saved and has no URI.
+*/
+LILV_API const LilvNode*
+lilv_state_get_uri(const LilvState* state);
+
+/**
+ Get the label of `state`.
+*/
+LILV_API const char*
+lilv_state_get_label(const LilvState* state);
+
+/**
+ Set the label of `state`.
+*/
+LILV_API void
+lilv_state_set_label(LilvState* state,
+ const char* label);
+
+/**
+ Set a metadata property on `state`.
+ @param state The state to set the metadata for.
+ @param key The key to store `value` under (URID).
+ @param value Pointer to the value to be stored.
+ @param size The size of `value` in bytes.
+ @param type The type of `value` (URID).
+ @param flags LV2_State_Flags for `value`.
+ @return 0 on success.
+
+ This is a generic version of lilv_state_set_label(), which sets metadata
+ properties visible to hosts, but not plugins. This allows storing useful
+ information such as comments or preset banks.
+*/
+LILV_API int
+lilv_state_set_metadata(LilvState* state,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags);
+
+/**
+ Function to set a port value.
+ @param port_symbol The symbol of the port.
+ @param user_data The user_data passed to lilv_state_restore().
+ @param size The size of `value`.
+ @param type The URID of the type of `value`.
+ @param value A pointer to the port value.
+*/
+typedef void (*LilvSetPortValueFunc)(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t size,
+ uint32_t type);
+
+/**
+ Enumerate the port values in a state snapshot.
+ @param state The state to retrieve port values from.
+ @param set_value A function to receive port values.
+ @param user_data User data to pass to `set_value`.
+
+ This function is a subset of lilv_state_restore() that only fires the
+ `set_value` callback and does not directly affect a plugin instance. This
+ is useful in hosts that need to retrieve the port values in a state snapshot
+ for special handling.
+*/
+LILV_API void
+lilv_state_emit_port_values(const LilvState* state,
+ LilvSetPortValueFunc set_value,
+ void* user_data);
+
+/**
+ Restore a plugin instance from a state snapshot.
+ @param state The state to restore, which must apply to the correct plugin.
+ @param instance An instance of the plugin `state` applies to, or NULL.
+ @param set_value A function to set a port value (may be NULL).
+ @param user_data User data to pass to `set_value`.
+ @param flags Bitwise OR of LV2_State_Flags values.
+ @param features Features to pass LV2_State_Interface.restore().
+
+ This will set all the properties of `instance`, if given, to the values
+ stored in `state`. If `set_value` is provided, it will be called (with the
+ given `user_data`) to restore each port value, otherwise the host must
+ restore the port values itself (using lilv_state_get_port_value()) in order
+ to completely restore `state`.
+
+ If the state has properties and `instance` is given, this function is in
+ the "instantiation" threading class, i.e. it MUST NOT be called
+ simultaneously with any function on the same plugin instance. If the state
+ has no properties, only port values are set via `set_value`.
+
+ See <a href="http://lv2plug.in/ns/ext/state/state.h">state.h</a> from the
+ LV2 State extension for details on the `flags` and `features` parameters.
+*/
+LILV_API void
+lilv_state_restore(const LilvState* state,
+ LilvInstance* instance,
+ LilvSetPortValueFunc set_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features);
+
+/**
+ Save state to a file.
+ @param world The world.
+ @param map URID mapper.
+ @param unmap URID unmapper.
+ @param state State to save.
+ @param uri URI of state, may be NULL.
+ @param dir Path of the bundle directory to save into.
+ @param filename Path of the state file relative to `dir`.
+
+ The format of state on disk is compatible with that defined in the LV2
+ preset extension, i.e. this function may be used to save presets which can
+ be loaded by any host.
+
+ If `uri` is NULL, the preset URI will be a file URI, but the bundle
+ can safely be moved (i.e. the state file will use "<>" as the subject).
+*/
+LILV_API int
+lilv_state_save(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* dir,
+ const char* filename);
+
+/**
+ Save state to a string. This function does not use the filesystem.
+
+ @param world The world.
+ @param map URID mapper.
+ @param unmap URID unmapper.
+ @param state The state to serialize.
+ @param uri URI for the state description (mandatory).
+ @param base_uri Base URI for serialisation. Unless you know what you are
+ doing, pass NULL for this, otherwise the state may not be restorable via
+ lilv_state_new_from_string().
+*/
+LILV_API char*
+lilv_state_to_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* base_uri);
+
+/**
+ Unload a state from the world and delete all associated files.
+ @param world The world.
+ @param state State to remove from the system.
+
+ This function DELETES FILES/DIRECTORIES FROM THE FILESYSTEM! It is intended
+ for removing user-saved presets, but can delete any state the user has
+ permission to delete, including presets shipped with plugins.
+
+ The rdfs:seeAlso file for the state will be removed. The entry in the
+ bundle's manifest.ttl is removed, and if this results in an empty manifest,
+ then the manifest file is removed. If this results in an empty bundle, then
+ the bundle directory is removed as well.
+*/
+LILV_API int
+lilv_state_delete(LilvWorld* world,
+ const LilvState* state);
+
+/**
+ @}
+ @name Scale Point
+ @{
+*/
+
+/**
+ Get the label of this scale point (enumeration value)
+ Returned value is owned by `point` and must not be freed.
+*/
+LILV_API const LilvNode*
+lilv_scale_point_get_label(const LilvScalePoint* point);
+
+/**
+ Get the value of this scale point (enumeration value)
+ Returned value is owned by `point` and must not be freed.
+*/
+LILV_API const LilvNode*
+lilv_scale_point_get_value(const LilvScalePoint* point);
+
+/**
+ @}
+ @name Plugin Class
+ @{
+*/
+
+/**
+ Get the URI of this class' superclass.
+ Returned value is owned by `plugin_class` and must not be freed by caller.
+ Returned value may be NULL, if class has no parent.
+*/
+LILV_API const LilvNode*
+lilv_plugin_class_get_parent_uri(const LilvPluginClass* plugin_class);
+
+/**
+ Get the URI of this plugin class.
+ Returned value is owned by `plugin_class` and must not be freed by caller.
+*/
+LILV_API const LilvNode*
+lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class);
+
+/**
+ Get the label of this plugin class, ie "Oscillators".
+ Returned value is owned by `plugin_class` and must not be freed by caller.
+*/
+LILV_API const LilvNode*
+lilv_plugin_class_get_label(const LilvPluginClass* plugin_class);
+
+/**
+ Get the subclasses of this plugin class.
+ Returned value must be freed by caller with lilv_plugin_classes_free().
+*/
+LILV_API LilvPluginClasses*
+lilv_plugin_class_get_children(const LilvPluginClass* plugin_class);
+
+/**
+ @}
+ @name Plugin Instance
+ @{
+*/
+
+/**
+ @cond LILV_DOCUMENT_INSTANCE_IMPL
+*/
+
+/* Instance of a plugin.
+ This is exposed in the ABI to allow inlining of performance critical
+ functions like lilv_instance_run() (simple wrappers of functions in lv2.h).
+ This is for performance reasons, user code should not use this definition
+ in any way (which is why it is not machine documented).
+ Truly private implementation details are hidden via `pimpl`.
+*/
+struct LilvInstanceImpl {
+ const LV2_Descriptor* lv2_descriptor;
+ LV2_Handle lv2_handle;
+ void* pimpl;
+};
+
+/**
+ @endcond
+*/
+
+/**
+ Instantiate a plugin.
+ The returned value is a lightweight handle for an LV2 plugin instance,
+ it does not refer to `plugin`, or any other Lilv state. The caller must
+ eventually free it with lilv_instance_free().
+ `features` is a NULL-terminated array of features the host supports.
+ NULL may be passed if the host supports no additional features.
+ @return NULL if instantiation failed.
+*/
+LILV_API LilvInstance*
+lilv_plugin_instantiate(const LilvPlugin* plugin,
+ double sample_rate,
+ const LV2_Feature*const* features);
+
+/**
+ Free a plugin instance.
+ It is safe to call this function on NULL.
+ `instance` is invalid after this call.
+*/
+LILV_API void
+lilv_instance_free(LilvInstance* instance);
+
+#ifndef LILV_INTERNAL
+
+/**
+ Get the URI of the plugin which `instance` is an instance of.
+ Returned string is shared and must not be modified or deleted.
+*/
+static inline const char*
+lilv_instance_get_uri(const LilvInstance* instance)
+{
+ return instance->lv2_descriptor->URI;
+}
+
+/**
+ Connect a port to a data location.
+ This may be called regardless of whether the plugin is activated,
+ activation and deactivation does not destroy port connections.
+*/
+static inline void
+lilv_instance_connect_port(LilvInstance* instance,
+ uint32_t port_index,
+ void* data_location)
+{
+ instance->lv2_descriptor->connect_port
+ (instance->lv2_handle, port_index, data_location);
+}
+
+/**
+ Activate a plugin instance.
+ This resets all state information in the plugin, except for port data
+ locations (as set by lilv_instance_connect_port()). This MUST be called
+ before calling lilv_instance_run().
+*/
+static inline void
+lilv_instance_activate(LilvInstance* instance)
+{
+ if (instance->lv2_descriptor->activate) {
+ instance->lv2_descriptor->activate(instance->lv2_handle);
+ }
+}
+
+/**
+ Run `instance` for `sample_count` frames.
+ If the hint lv2:hardRTCapable is set for this plugin, this function is
+ guaranteed not to block.
+*/
+static inline void
+lilv_instance_run(LilvInstance* instance,
+ uint32_t sample_count)
+{
+ instance->lv2_descriptor->run(instance->lv2_handle, sample_count);
+}
+
+/**
+ Deactivate a plugin instance.
+ Note that to run the plugin after this you must activate it, which will
+ reset all state information (except port connections).
+*/
+static inline void
+lilv_instance_deactivate(LilvInstance* instance)
+{
+ if (instance->lv2_descriptor->deactivate) {
+ instance->lv2_descriptor->deactivate(instance->lv2_handle);
+ }
+}
+
+/**
+ Get extension data from the plugin instance.
+ The type and semantics of the data returned is specific to the particular
+ extension, though in all cases it is shared and must not be deleted.
+*/
+static inline const void*
+lilv_instance_get_extension_data(const LilvInstance* instance,
+ const char* uri)
+{
+ if (instance->lv2_descriptor->extension_data) {
+ return instance->lv2_descriptor->extension_data(uri);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ Get the LV2_Descriptor of the plugin instance.
+ Normally hosts should not need to access the LV2_Descriptor directly,
+ use the lilv_instance_* functions.
+
+ The returned descriptor is shared and must not be deleted.
+*/
+static inline const LV2_Descriptor*
+lilv_instance_get_descriptor(const LilvInstance* instance)
+{
+ return instance->lv2_descriptor;
+}
+
+/**
+ Get the LV2_Handle of the plugin instance.
+ Normally hosts should not need to access the LV2_Handle directly,
+ use the lilv_instance_* functions.
+
+ The returned handle is shared and must not be deleted.
+*/
+static inline LV2_Handle
+lilv_instance_get_handle(const LilvInstance* instance)
+{
+ return instance->lv2_handle;
+}
+
+#endif /* LILV_INTERNAL */
+
+/**
+ @}
+ @name Plugin UI
+ @{
+*/
+
+/**
+ Get all UIs for `plugin`.
+ Returned value must be freed by caller using lilv_uis_free().
+*/
+LILV_API LilvUIs*
+lilv_plugin_get_uis(const LilvPlugin* plugin);
+
+/**
+ Get the URI of a Plugin UI.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_ui_get_uri(const LilvUI* ui);
+
+/**
+ Get the types (URIs of RDF classes) of a Plugin UI.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+
+ Note that in most cases lilv_ui_is_supported() should be used, which avoids
+ the need to use this function (and type specific logic).
+*/
+LILV_API const LilvNodes*
+lilv_ui_get_classes(const LilvUI* ui);
+
+/**
+ Check whether a plugin UI has a given type.
+ @param ui The Plugin UI
+ @param class_uri The URI of the LV2 UI type to check this UI against
+*/
+LILV_API bool
+lilv_ui_is_a(const LilvUI* ui, const LilvNode* class_uri);
+
+/**
+ Function to determine whether a UI type is supported.
+
+ This is provided by the user and must return non-zero iff using a UI of type
+ `ui_type_uri` in a container of type `container_type_uri` is supported.
+*/
+typedef unsigned (*LilvUISupportedFunc)(const char* container_type_uri,
+ const char* ui_type_uri);
+
+/**
+ Return true iff a Plugin UI is supported as a given widget type.
+ @param ui The Plugin UI
+ @param supported_func User provided supported predicate.
+ @param container_type The widget type to host the UI within.
+ @param ui_type (Output) If non-NULL, set to the native type of the UI
+ which is owned by `ui` and must not be freed by the caller.
+ @return The embedding quality level returned by `supported_func`.
+*/
+LILV_API unsigned
+lilv_ui_is_supported(const LilvUI* ui,
+ LilvUISupportedFunc supported_func,
+ const LilvNode* container_type,
+ const LilvNode** ui_type);
+
+/**
+ Get the URI for a Plugin UI's bundle.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_ui_get_bundle_uri(const LilvUI* ui);
+
+/**
+ Get the URI for a Plugin UI's shared library.
+ @param ui The Plugin UI
+ @return a shared value which must not be modified or freed.
+*/
+LILV_API const LilvNode*
+lilv_ui_get_binary_uri(const LilvUI* ui);
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* LILV_LILV_H */
diff --git a/lilv/lilvmm.hpp b/lilv/lilvmm.hpp
new file mode 100644
index 0000000..30ec274
--- /dev/null
+++ b/lilv/lilvmm.hpp
@@ -0,0 +1,345 @@
+/*
+ Copyright 2007-2014 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 {
+
+LILV_DEPRECATED
+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_WRAP3(RT, prefix, name, T1, a1, T2, a2, T3, a3) \
+ inline RT name(T1 a1, T2 a2, T3 a3) { \
+ return lilv_ ## prefix ## _ ## name(me, a1, a2, a3); \
+ }
+
+#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(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 UI {
+ inline UI(const LilvUI* c_obj) : me(c_obj) {}
+ LILV_WRAP_CONVERSION(const LilvUI);
+
+ LILV_WRAP0(const LilvNode*, ui, get_uri);
+ LILV_WRAP0(const LilvNode*, ui, get_bundle_uri);
+ LILV_WRAP0(const LilvNode*, ui, get_binary_uri);
+ LILV_WRAP0(const LilvNodes*, ui, get_classes);
+ /*LILV_WRAP3(bool, ui, is_supported,
+ LilvUISupportedFunc, supported_func,
+ const LilvNode*, container_type,
+ const LilvNode**, ui_type);*/
+ LILV_WRAP1(bool, ui, is_a, const LilvNode*, class_uri);
+
+ const LilvUI* me;
+};
+
+struct UIs {
+ LILV_WRAP_COLL(UIs, UI, uis);
+};
+
+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 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);
+ LILV_WRAP1(Nodes, plugin, get_related, Node, type);
+
+ 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, ...) {
+ va_list args;
+ va_start(args, class_1);
+
+ const uint32_t count = lilv_plugin_get_num_ports_of_class_va(
+ me, class_1, args);
+
+ va_end(args);
+ return count;
+ }
+
+ 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);
+ LILV_WRAP1(int, world, load_resource, const LilvNode*, resource);
+
+ LilvWorld* me;
+};
+
+} /* namespace Lilv */
+
+#endif /* LILV_LILVMM_HPP */
diff --git a/src/collections.c b/src/collections.c
new file mode 100644
index 0000000..9ded845
--- /dev/null
+++ b/src/collections.c
@@ -0,0 +1,224 @@
+/*
+ Copyright 2008-2014 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* collection)
+{
+ if (collection) {
+ zix_tree_free((ZixTree*)collection);
+ }
+}
+
+unsigned
+lilv_collection_size(const LilvCollection* collection)
+{
+ return (collection ? zix_tree_size((const ZixTree*)collection) : 0);
+}
+
+LilvIter*
+lilv_collection_begin(const LilvCollection* collection)
+{
+ return collection ? (LilvIter*)zix_tree_begin((ZixTree*)collection) : NULL;
+}
+
+void*
+lilv_collection_get(const LilvCollection* collection,
+ const LilvIter* i)
+{
+ return zix_tree_get((const 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* classes,
+ const LilvNode* uri)
+{
+ return (LilvPluginClass*)lilv_collection_get_by_uri(
+ (const ZixTree*)classes, uri);
+}
+
+LILV_API const LilvUI*
+lilv_uis_get_by_uri(const LilvUIs* uis, const LilvNode* uri)
+{
+ return (LilvUI*)lilv_collection_get_by_uri((const ZixTree*)uis, 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* plugins, const LilvNode* uri)
+{
+ return (LilvPlugin*)lilv_collection_get_by_uri(
+ (const ZixTree*)plugins, uri);
+}
+
+/* Nodes */
+
+LILV_API bool
+lilv_nodes_contains(const LilvNodes* nodes, const LilvNode* value)
+{
+ LILV_FOREACH(nodes, i, nodes) {
+ if (lilv_node_equals(lilv_nodes_get(nodes, i), value)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+LILV_API LilvNodes*
+lilv_nodes_merge(const LilvNodes* a, const LilvNodes* b)
+{
+ LilvNodes* result = lilv_nodes_new();
+
+ LILV_FOREACH(nodes, i, a)
+ zix_tree_insert((ZixTree*)result,
+ lilv_node_duplicate(lilv_nodes_get(a, i)),
+ NULL);
+
+ LILV_FOREACH(nodes, i, b)
+ zix_tree_insert((ZixTree*)result,
+ lilv_node_duplicate(lilv_nodes_get(b, i)),
+ NULL);
+
+ return result;
+}
+
+/* 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));
+}
diff --git a/src/instance.c b/src/instance.c
new file mode 100644
index 0000000..030c6ff
--- /dev/null
+++ b/src/instance.c
@@ -0,0 +1,110 @@
+/*
+ Copyright 2007-2016 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 <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);
+ if (plugin->parse_errors) {
+ return NULL;
+ }
+
+ LilvInstance* result = NULL;
+ const LilvNode* const lib_uri = lilv_plugin_get_library_uri(plugin);
+ const LilvNode* const bundle_uri = lilv_plugin_get_bundle_uri(plugin);
+ if (!lib_uri || !bundle_uri) {
+ return NULL;
+ }
+
+ char* const bundle_path = lilv_file_uri_parse(
+ lilv_node_as_uri(bundle_uri), NULL);
+
+ LilvLib* lib = lilv_lib_open(plugin->world, lib_uri, bundle_path, features);
+ if (!lib) {
+ lilv_free(bundle_path);
+ return NULL;
+ }
+
+ const LV2_Feature** local_features = NULL;
+ if (features == NULL) {
+ local_features = (const LV2_Feature**)malloc(sizeof(LV2_Feature*));
+ local_features[0] = 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
+ }
+
+ if (!strcmp(ld->URI, 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;
+ break;
+ }
+ }
+
+ free(local_features);
+ lilv_free(bundle_path);
+
+ if (result) {
+ if (result->lv2_handle == NULL) {
+ // Failed to instantiate
+ free(result);
+ lilv_lib_close(lib);
+ 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);
+ }
+ }
+
+ 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);
+}
diff --git a/src/lib.c b/src/lib.c
new file mode 100644
index 0000000..73e57bb
--- /dev/null
+++ b/src/lib.c
@@ -0,0 +1,112 @@
+/*
+ Copyright 2012-2016 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 LilvLib key = {
+ world, (LilvNode*)uri, (char*)bundle_path, NULL, NULL, NULL, 0
+ };
+ 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);
+ char* const lib_path = lilv_file_uri_parse(lib_uri, NULL);
+ 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());
+ lilv_free(lib_path);
+ return NULL;
+ }
+
+ LV2_Descriptor_Function df = (LV2_Descriptor_Function)
+ lilv_dlfunc(lib, "lv2_descriptor");
+
+ 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 %s:lv2_lib_descriptor failed\n", lib_path);
+ dlclose(lib);
+ lilv_free(lib_path);
+ return NULL;
+ }
+ } else if (!df) {
+ LILV_ERRORF("No `lv2_descriptor' or `lv2_lib_descriptor' in %s\n",
+ lib_path);
+ dlclose(lib);
+ lilv_free(lib_path);
+ return NULL;
+ }
+ lilv_free(lib_path);
+
+ LilvLib* llib = (LilvLib*)malloc(sizeof(LilvLib));
+ llib->world = world;
+ llib->uri = lilv_node_duplicate(uri);
+ llib->bundle_path = lilv_strdup(bundle_path);
+ llib->lib = lib;
+ llib->lv2_descriptor = df;
+ llib->desc = desc;
+ 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);
+ } else if (lib->desc) {
+ return lib->desc->get_plugin(lib->desc->handle, index);
+ }
+ 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->bundle_path);
+ free(lib);
+ }
+}
diff --git a/src/lilv_internal.h b/src/lilv_internal.h
new file mode 100644
index 0000000..15d2716
--- /dev/null
+++ b/src/lilv_internal.h
@@ -0,0 +1,444 @@
+/*
+ Copyright 2007-2016 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>
+# include <direct.h>
+# include <stdio.h>
+# define dlopen(path, flags) LoadLibrary(path)
+# define dlclose(lib) FreeLibrary((HMODULE)lib)
+# define unlink(path) _unlink(path)
+# define rmdir(path) _rmdir(path)
+# ifdef _MSC_VER
+# define __func__ __FUNCTION__
+# ifndef snprintf
+# define snprintf _snprintf
+# endif
+# endif
+#ifndef INFINITY
+# define INFINITY DBL_MAX + DBL_MAX
+#endif
+#ifndef NAN
+# define NAN INFINITY - INFINITY
+#endif
+static inline const char* dlerror(void) { return "Unknown error"; }
+#else
+# include <dlfcn.h>
+# include <unistd.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 {
+ LilvNode* 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;
+ char* bundle_path;
+ void* lib;
+ LV2_Descriptor_Function lv2_descriptor;
+ const LV2_Lib_Descriptor* desc;
+ 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 parse_errors;
+ 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;
+ LilvPlugins* zombies;
+ 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_latency;
+ SordNode* lv2_maximum;
+ SordNode* lv2_microVersion;
+ SordNode* lv2_minimum;
+ SordNode* lv2_minorVersion;
+ SordNode* lv2_name;
+ SordNode* lv2_optionalFeature;
+ SordNode* lv2_port;
+ SordNode* lv2_portProperty;
+ SordNode* lv2_reportsLatency;
+ SordNode* lv2_requiredFeature;
+ SordNode* lv2_symbol;
+ SordNode* lv2_prototype;
+ SordNode* owl_Ontology;
+ 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;
+ SordNode* null_uri;
+ } 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;
+};
+
+typedef struct LilvVersion {
+ int minor;
+ int micro;
+} LilvVersion;
+
+/*
+ *
+ * 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_clear(LilvPlugin* plugin, LilvNode* bundle_uri);
+void lilv_plugin_load_if_necessary(const LilvPlugin* plugin);
+void lilv_plugin_free(LilvPlugin* plugin);
+LilvNode* lilv_plugin_get_unique(const LilvPlugin* plugin,
+ 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_node,
+ 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);
+
+LilvNode* lilv_world_get_manifest_uri(LilvWorld* world,
+ const LilvNode* bundle_uri);
+
+const uint8_t* lilv_world_blank_node_prefix(LilvWorld* world);
+
+SerdStatus lilv_world_load_file(LilvWorld* world,
+ SerdReader* reader,
+ const LilvNode* uri);
+
+SerdStatus
+lilv_world_load_graph(LilvWorld* world,
+ SordNode* graph,
+ const LilvNode* 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* str);
+LilvNode* lilv_node_new_from_node(LilvWorld* world, const SordNode* node);
+
+int lilv_header_compare_by_uri(const void* a, const void* b, void* user_data);
+int lilv_lib_compare(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);
+
+static inline int
+lilv_version_cmp(const LilvVersion* a, const LilvVersion* b)
+{
+ if (a->minor == b->minor && a->micro == b->micro) {
+ return 0;
+ } else if ((a->minor < b->minor)
+ || (a->minor == b->minor && a->micro < b->micro)) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+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);
+
+bool
+lilv_world_ask_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object);
+
+LilvNodes*
+lilv_world_find_nodes_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object);
+
+SordModel*
+lilv_world_filter_model(LilvWorld* world,
+ SordModel* model,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object,
+ const SordNode* graph);
+
+#define FOREACH_MATCH(iter) \
+ for (; !sord_iter_end(iter); sord_iter_next(iter))
+
+LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* world,
+ 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* dir_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 (*LilvVoidFunc)(void);
+
+/** dlsym wrapper to return a function pointer (without annoying warning) */
+static inline LilvVoidFunc
+lilv_dlfunc(void* handle, const char* symbol)
+{
+#ifdef _WIN32
+ return (LilvVoidFunc)GetProcAddress((HMODULE)handle, symbol);
+#else
+ typedef LilvVoidFunc (*VoidFuncGetter)(void*, const char*);
+ VoidFuncGetter dlfunc = (VoidFuncGetter)dlsym;
+ return dlfunc(handle, symbol);
+#endif
+}
+
+#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__)
+#define LILV_NOTE(str) fprintf(stderr, "%s(): note: " str, \
+ __func__)
+#define LILV_NOTEF(fmt, ...) fprintf(stderr, "%s(): note: " fmt, \
+ __func__, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LILV_INTERNAL_H */
diff --git a/src/node.c b/src/node.c
new file mode 100644
index 0000000..5605177
--- /dev/null
+++ b/src/node.c
@@ -0,0 +1,397 @@
+/*
+ Copyright 2007-2016 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 <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_internal.h"
+
+static void
+lilv_node_set_numerics_from_string(LilvNode* val)
+{
+ const char* str = (const char*)sord_node_get_string(val->node);
+
+ 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, NULL, 10);
+ break;
+ case LILV_VALUE_FLOAT:
+ val->val.float_val = serd_strtod(str, NULL);
+ break;
+ case LILV_VALUE_BOOL:
+ val->val.bool_val = !strcmp(str, "true");
+ break;
+ }
+}
+
+/** Note that if `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;
+ }
+
+ if (!val->node) {
+ free(val);
+ return NULL;
+ }
+
+ return val;
+}
+
+/** Create a new LilvNode from `node`, or return NULL if impossible */
+LilvNode*
+lilv_node_new_from_node(LilvWorld* world, const SordNode* node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ LilvNode* result = NULL;
+ SordNode* datatype_uri = NULL;
+ LilvNodeType type = LILV_VALUE_STRING;
+
+ switch (sord_node_get_type(node)) {
+ case SORD_URI:
+ result = (LilvNode*)malloc(sizeof(LilvNode));
+ result->world = world;
+ result->type = LILV_VALUE_URI;
+ result->node = sord_node_copy(node);
+ break;
+ case SORD_BLANK:
+ result = (LilvNode*)malloc(sizeof(LilvNode));
+ result->world = 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(node));
+ lilv_node_set_numerics_from_string(result);
+ break;
+ }
+
+ 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_file_uri(LilvWorld* world, const char* host, const char* path)
+{
+ char* abs_path = lilv_path_absolute(path);
+ SerdNode s = serd_node_new_file_uri(
+ (const uint8_t*)abs_path, (const uint8_t*)host, NULL, true);
+
+ LilvNode* ret = lilv_node_new(world, LILV_VALUE_URI, (const char*)s.buf);
+ serd_node_free(&s);
+ free(abs_path);
+ return ret;
+}
+
+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)
+{
+ return (lilv_node_is_uri(value)
+ ? (const char*)sord_node_get_string(value->node)
+ : NULL);
+}
+
+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)
+{
+ return (lilv_node_is_blank(value)
+ ? (const char*)sord_node_get_string(value->node)
+ : NULL);
+}
+
+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:
+ case LILV_VALUE_BLOB:
+ 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)
+{
+ return lilv_node_is_int(value) ? value->val.int_val : 0;
+}
+
+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)
+{
+ if (lilv_node_is_float(value)) {
+ return value->val.float_val;
+ } else if (lilv_node_is_int(value)) {
+ return (float)value->val.int_val;
+ }
+ return NAN;
+}
+
+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)
+{
+ return lilv_node_is_bool(value) ? value->val.bool_val : false;
+}
+
+LILV_API char*
+lilv_node_get_path(const LilvNode* value, char** hostname)
+{
+ if (lilv_node_is_uri(value)) {
+ return lilv_file_uri_parse(lilv_node_as_uri(value), hostname);
+ }
+ return NULL;
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644
index 0000000..0575b32
--- /dev/null
+++ b/src/plugin.c
@@ -0,0 +1,1138 @@
+/*
+ Copyright 2007-2016 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 __STDC_LIMIT_MACROS
+
+#include <assert.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv_config.h"
+#include "lilv_internal.h"
+
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+#define NS_DOAP (const uint8_t*)"http://usefulinc.com/ns/doap#"
+#define NS_FOAF (const uint8_t*)"http://xmlns.com/foaf/0.1/"
+
+static void
+lilv_plugin_init(LilvPlugin* plugin, LilvNode* bundle_uri)
+{
+ plugin->bundle_uri = bundle_uri;
+ plugin->binary_uri = NULL;
+#ifdef LILV_DYN_MANIFEST
+ plugin->dynmanifest = NULL;
+#endif
+ plugin->plugin_class = NULL;
+ plugin->data_uris = lilv_nodes_new();
+ plugin->ports = NULL;
+ plugin->num_ports = 0;
+ plugin->loaded = false;
+ plugin->parse_errors = false;
+ plugin->replaced = false;
+}
+
+/** Ownership of `uri` and `bundle` is taken */
+LilvPlugin*
+lilv_plugin_new(LilvWorld* world, LilvNode* uri, LilvNode* bundle_uri)
+{
+ LilvPlugin* plugin = (LilvPlugin*)malloc(sizeof(LilvPlugin));
+
+ plugin->world = world;
+ plugin->plugin_uri = uri;
+
+ lilv_plugin_init(plugin, bundle_uri);
+ return plugin;
+}
+
+void
+lilv_plugin_clear(LilvPlugin* plugin, LilvNode* bundle_uri)
+{
+ lilv_node_free(plugin->bundle_uri);
+ lilv_node_free(plugin->binary_uri);
+ lilv_nodes_free(plugin->data_uris);
+ lilv_plugin_init(plugin, bundle_uri);
+}
+
+static void
+lilv_plugin_free_ports(LilvPlugin* plugin)
+{
+ if (plugin->ports) {
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ lilv_port_free(plugin, plugin->ports[i]);
+ }
+ free(plugin->ports);
+ plugin->num_ports = 0;
+ plugin->ports = NULL;
+ }
+}
+
+void
+lilv_plugin_free(LilvPlugin* plugin)
+{
+#ifdef LILV_DYN_MANIFEST
+ if (plugin->dynmanifest && --plugin->dynmanifest->refs == 0) {
+ typedef int (*CloseFunc)(LV2_Dyn_Manifest_Handle);
+ CloseFunc close_func = (CloseFunc)lilv_dlfunc(plugin->dynmanifest->lib,
+ "lv2_dyn_manifest_close");
+ if (close_func) {
+ close_func(plugin->dynmanifest->handle);
+ }
+
+ dlclose(plugin->dynmanifest->lib);
+ lilv_node_free(plugin->dynmanifest->bundle);
+ free(plugin->dynmanifest);
+ }
+#endif
+
+ lilv_node_free(plugin->plugin_uri);
+ plugin->plugin_uri = NULL;
+
+ lilv_node_free(plugin->bundle_uri);
+ plugin->bundle_uri = NULL;
+
+ lilv_node_free(plugin->binary_uri);
+ plugin->binary_uri = NULL;
+
+ lilv_plugin_free_ports(plugin);
+
+ lilv_nodes_free(plugin->data_uris);
+ plugin->data_uris = NULL;
+
+ free(plugin);
+}
+
+static LilvNode*
+lilv_plugin_get_one(const LilvPlugin* plugin,
+ const SordNode* subject,
+ const SordNode* predicate)
+{
+ LilvNode* ret = NULL;
+ SordIter* stream = lilv_world_query_internal(
+ plugin->world, subject, predicate, NULL);
+ if (!sord_iter_end(stream)) {
+ ret = lilv_node_new_from_node(plugin->world,
+ sord_iter_get_node(stream, SORD_OBJECT));
+ }
+ sord_iter_free(stream);
+ return ret;
+}
+
+LilvNode*
+lilv_plugin_get_unique(const LilvPlugin* plugin,
+ const SordNode* subject,
+ const SordNode* predicate)
+{
+ LilvNode* ret = lilv_plugin_get_one(plugin, subject, predicate);
+ if (!ret) {
+ LILV_ERRORF("No value found for (%s %s ...) property\n",
+ sord_node_get_string(subject),
+ sord_node_get_string(predicate));
+ }
+ return ret;
+}
+
+static void
+lilv_plugin_load(LilvPlugin* plugin)
+{
+ SordNode* bundle_uri_node = plugin->bundle_uri->node;
+ const SerdNode* bundle_uri_snode = sord_node_to_serd_node(bundle_uri_node);
+
+ SerdEnv* env = serd_env_new(bundle_uri_snode);
+ SerdReader* reader = sord_new_reader(plugin->world->model, env, SERD_TURTLE,
+ bundle_uri_node);
+
+ SordModel* prots = lilv_world_filter_model(
+ plugin->world,
+ plugin->world->model,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_prototype,
+ NULL, NULL);
+ SordModel* skel = sord_new(plugin->world->world, SORD_SPO, false);
+ SordIter* iter = sord_begin(prots);
+ for (; !sord_iter_end(iter); sord_iter_next(iter)) {
+ const SordNode* t = sord_iter_get_node(iter, SORD_OBJECT);
+ LilvNode* prototype = lilv_node_new_from_node(plugin->world, t);
+
+ lilv_world_load_resource(plugin->world, prototype);
+
+ SordIter* statements = sord_search(
+ plugin->world->model, prototype->node, NULL, NULL, NULL);
+ FOREACH_MATCH(statements) {
+ SordQuad quad;
+ sord_iter_get(statements, quad);
+ quad[0] = plugin->plugin_uri->node;
+ sord_add(skel, quad);
+ }
+
+ sord_iter_free(statements);
+ lilv_node_free(prototype);
+ }
+ sord_iter_free(iter);
+
+ for (iter = sord_begin(skel); !sord_iter_end(iter); sord_iter_next(iter)) {
+ SordQuad quad;
+ sord_iter_get(iter, quad);
+ sord_add(plugin->world->model, quad);
+ }
+ sord_iter_free(iter);
+ sord_free(skel);
+ sord_free(prots);
+
+ // Parse all the plugin's data files into RDF model
+ SerdStatus st = SERD_SUCCESS;
+ LILV_FOREACH(nodes, i, plugin->data_uris) {
+ const LilvNode* data_uri = lilv_nodes_get(plugin->data_uris, i);
+
+ serd_env_set_base_uri(env, sord_node_to_serd_node(data_uri->node));
+ st = lilv_world_load_file(plugin->world, reader, data_uri);
+ if (st > SERD_FAILURE) {
+ break;
+ }
+ }
+
+ if (st > SERD_FAILURE) {
+ plugin->loaded = true;
+ plugin->parse_errors = true;
+ serd_reader_free(reader);
+ serd_env_free(env);
+ return;
+ }
+
+#ifdef LILV_DYN_MANIFEST
+ // Load and parse dynamic manifest data, if this is a library
+ if (plugin->dynmanifest) {
+ typedef int (*GetDataFunc)(LV2_Dyn_Manifest_Handle handle,
+ FILE* fp,
+ const char* uri);
+ GetDataFunc get_data_func = (GetDataFunc)lilv_dlfunc(
+ plugin->dynmanifest->lib, "lv2_dyn_manifest_get_data");
+ if (get_data_func) {
+ const SordNode* bundle = plugin->dynmanifest->bundle->node;
+ serd_env_set_base_uri(env, sord_node_to_serd_node(bundle));
+ FILE* fd = tmpfile();
+ get_data_func(plugin->dynmanifest->handle, fd,
+ lilv_node_as_string(plugin->plugin_uri));
+ rewind(fd);
+ serd_reader_add_blank_prefix(
+ reader, lilv_world_blank_node_prefix(plugin->world));
+ serd_reader_read_file_handle(
+ reader, fd, (const uint8_t*)"(dyn-manifest)");
+ fclose(fd);
+ }
+ }
+#endif
+ serd_reader_free(reader);
+ serd_env_free(env);
+
+ plugin->loaded = true;
+}
+
+static bool
+is_symbol(const char* str)
+{
+ for (const char* s = str; *s; ++s) {
+ if (!((*s >= 'a' && *s <= 'z') ||
+ (*s >= 'A' && *s <= 'Z') ||
+ (s > str && *s >= '0' && *s <= '9') ||
+ *s == '_')) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void
+lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_plugin)
+{
+ LilvPlugin* plugin = (LilvPlugin*)const_plugin;
+
+ lilv_plugin_load_if_necessary(plugin);
+
+ if (!plugin->ports) {
+ plugin->ports = (LilvPort**)malloc(sizeof(LilvPort*));
+ plugin->ports[0] = NULL;
+
+ SordIter* ports = lilv_world_query_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_port,
+ NULL);
+
+ FOREACH_MATCH(ports) {
+ const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
+ LilvNode* index = lilv_plugin_get_unique(
+ plugin, port, plugin->world->uris.lv2_index);
+ LilvNode* symbol = lilv_plugin_get_unique(
+ plugin, port, plugin->world->uris.lv2_symbol);
+
+ if (!lilv_node_is_string(symbol) ||
+ !is_symbol((const char*)sord_node_get_string(symbol->node))) {
+ LILV_ERRORF("Plugin <%s> port symbol `%s' is invalid\n",
+ lilv_node_as_uri(plugin->plugin_uri),
+ lilv_node_as_string(symbol));
+ lilv_node_free(symbol);
+ lilv_node_free(index);
+ lilv_plugin_free_ports(plugin);
+ break;
+ }
+
+ if (!lilv_node_is_int(index)) {
+ LILV_ERRORF("Plugin <%s> port index is not an integer\n",
+ lilv_node_as_uri(plugin->plugin_uri));
+ lilv_node_free(symbol);
+ lilv_node_free(index);
+ lilv_plugin_free_ports(plugin);
+ break;
+ }
+
+ uint32_t this_index = lilv_node_as_int(index);
+ LilvPort* this_port = NULL;
+ if (plugin->num_ports > this_index) {
+ this_port = plugin->ports[this_index];
+ } else {
+ plugin->ports = (LilvPort**)realloc(
+ plugin->ports, (this_index + 1) * sizeof(LilvPort*));
+ memset(plugin->ports + plugin->num_ports, '\0',
+ (this_index - plugin->num_ports) * sizeof(LilvPort*));
+ plugin->num_ports = this_index + 1;
+ }
+
+ // Havn't seen this port yet, add it to array
+ if (!this_port) {
+ this_port = lilv_port_new(plugin->world,
+ port,
+ this_index,
+ lilv_node_as_string(symbol));
+ plugin->ports[this_index] = this_port;
+ }
+
+ SordIter* types = lilv_world_query_internal(
+ plugin->world, port, plugin->world->uris.rdf_a, NULL);
+ FOREACH_MATCH(types) {
+ const SordNode* type = sord_iter_get_node(types, SORD_OBJECT);
+ if (sord_node_get_type(type) == SORD_URI) {
+ zix_tree_insert(
+ (ZixTree*)this_port->classes,
+ lilv_node_new_from_node(plugin->world, type), NULL);
+ } else {
+ LILV_WARNF("Plugin <%s> port type is not a URI\n",
+ lilv_node_as_uri(plugin->plugin_uri));
+ }
+ }
+ sord_iter_free(types);
+
+ lilv_node_free(symbol);
+ lilv_node_free(index);
+ }
+ sord_iter_free(ports);
+
+ // Check sanity
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ if (!plugin->ports[i]) {
+ LILV_ERRORF("Plugin <%s> is missing port %d/%d\n",
+ lilv_node_as_uri(plugin->plugin_uri), i, plugin->num_ports);
+ lilv_plugin_free_ports(plugin);
+ break;
+ }
+ }
+ }
+}
+
+void
+lilv_plugin_load_if_necessary(const LilvPlugin* plugin)
+{
+ if (!plugin->loaded) {
+ lilv_plugin_load((LilvPlugin*)plugin);
+ }
+}
+
+LILV_API const LilvNode*
+lilv_plugin_get_uri(const LilvPlugin* plugin)
+{
+ return plugin->plugin_uri;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_get_bundle_uri(const LilvPlugin* plugin)
+{
+ return plugin->bundle_uri;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_get_library_uri(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary((LilvPlugin*)plugin);
+ if (!plugin->binary_uri) {
+ // <plugin> lv2:binary ?binary
+ SordIter* i = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_binary,
+ NULL);
+ FOREACH_MATCH(i) {
+ const SordNode* binary_node = sord_iter_get_node(i, SORD_OBJECT);
+ if (sord_node_get_type(binary_node) == SORD_URI) {
+ ((LilvPlugin*)plugin)->binary_uri =
+ lilv_node_new_from_node(plugin->world, binary_node);
+ break;
+ }
+ }
+ sord_iter_free(i);
+ }
+ if (!plugin->binary_uri) {
+ LILV_WARNF("Plugin <%s> has no lv2:binary\n",
+ lilv_node_as_uri(lilv_plugin_get_uri(plugin)));
+ }
+ return plugin->binary_uri;
+}
+
+LILV_API const LilvNodes*
+lilv_plugin_get_data_uris(const LilvPlugin* plugin)
+{
+ return plugin->data_uris;
+}
+
+LILV_API const LilvPluginClass*
+lilv_plugin_get_class(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary((LilvPlugin*)plugin);
+ if (!plugin->plugin_class) {
+ // <plugin> a ?class
+ SordIter* c = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.rdf_a,
+ NULL);
+ FOREACH_MATCH(c) {
+ const SordNode* class_node = sord_iter_get_node(c, SORD_OBJECT);
+ if (sord_node_get_type(class_node) != SORD_URI) {
+ continue;
+ }
+
+ LilvNode* klass = lilv_node_new_from_node(plugin->world, class_node);
+ if (!lilv_node_equals(klass, plugin->world->lv2_plugin_class->uri)) {
+ const LilvPluginClass* pclass = lilv_plugin_classes_get_by_uri(
+ plugin->world->plugin_classes, klass);
+
+ if (pclass) {
+ ((LilvPlugin*)plugin)->plugin_class = pclass;
+ lilv_node_free(klass);
+ break;
+ }
+ }
+
+ lilv_node_free(klass);
+ }
+ sord_iter_free(c);
+
+ if (plugin->plugin_class == NULL) {
+ ((LilvPlugin*)plugin)->plugin_class =
+ plugin->world->lv2_plugin_class;
+ }
+ }
+ return plugin->plugin_class;
+}
+
+static LilvNodes*
+lilv_plugin_get_value_internal(const LilvPlugin* plugin,
+ const SordNode* predicate)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes_internal(
+ plugin->world, plugin->plugin_uri->node, predicate, NULL);
+}
+
+LILV_API bool
+lilv_plugin_verify(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ if (plugin->parse_errors) {
+ return false;
+ }
+
+ LilvNode* rdf_type = lilv_new_uri(plugin->world, LILV_NS_RDF "type");
+ LilvNodes* results = lilv_plugin_get_value(plugin, rdf_type);
+ lilv_node_free(rdf_type);
+ if (!results) {
+ return false;
+ }
+
+ lilv_nodes_free(results);
+ results = lilv_plugin_get_value_internal(plugin,
+ plugin->world->uris.doap_name);
+ if (!results) {
+ return false;
+ }
+
+ lilv_nodes_free(results);
+ LilvNode* lv2_port = lilv_new_uri(plugin->world, LV2_CORE__port);
+ results = lilv_plugin_get_value(plugin, lv2_port);
+ lilv_node_free(lv2_port);
+ if (!results) {
+ return false;
+ }
+
+ lilv_nodes_free(results);
+ return true;
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_name(const LilvPlugin* plugin)
+{
+ LilvNodes* results = lilv_plugin_get_value_internal(
+ plugin, plugin->world->uris.doap_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> has no (mandatory) doap:name\n",
+ lilv_node_as_string(lilv_plugin_get_uri(plugin)));
+ }
+
+ return ret;
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_value(const LilvPlugin* plugin,
+ const LilvNode* predicate)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes(plugin->world, plugin->plugin_uri, predicate, NULL);
+}
+
+LILV_API uint32_t
+lilv_plugin_get_num_ports(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ return plugin->num_ports;
+}
+
+LILV_API void
+lilv_plugin_get_port_ranges_float(const LilvPlugin* plugin,
+ float* min_values,
+ float* max_values,
+ float* def_values)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ LilvNode* min = NULL;
+ LilvNode* max = NULL;
+ LilvNode* def = NULL;
+ LilvNode** minptr = min_values ? &min : NULL;
+ LilvNode** maxptr = max_values ? &max : NULL;
+ LilvNode** defptr = def_values ? &def : NULL;
+
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ lilv_port_get_range(plugin, plugin->ports[i], defptr, minptr, maxptr);
+
+ if (min_values) {
+ if (lilv_node_is_float(min) || lilv_node_is_int(min)) {
+ min_values[i] = lilv_node_as_float(min);
+ } else {
+ min_values[i] = NAN;
+ }
+ }
+
+ if (max_values) {
+ if (lilv_node_is_float(max) || lilv_node_is_int(max)) {
+ max_values[i] = lilv_node_as_float(max);
+ } else {
+ max_values[i] = NAN;
+ }
+ }
+
+ if (def_values) {
+ if (lilv_node_is_float(def) || lilv_node_is_int(def)) {
+ def_values[i] = lilv_node_as_float(def);
+ } else {
+ def_values[i] = NAN;
+ }
+ }
+
+ lilv_node_free(def);
+ lilv_node_free(min);
+ lilv_node_free(max);
+ }
+}
+
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class_va(const LilvPlugin* plugin,
+ const LilvNode* class_1,
+ va_list args)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+
+ uint32_t count = 0;
+
+ // Build array of classes from args so we can walk it several times
+ size_t n_classes = 0;
+ const LilvNode** classes = NULL;
+ for (LilvNode* c = NULL; (c = va_arg(args, LilvNode*)); ) {
+ classes = (const LilvNode**)realloc(
+ classes, ++n_classes * sizeof(LilvNode*));
+ classes[n_classes - 1] = c;
+ }
+
+ // Check each port against every type
+ for (unsigned i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ if (port && lilv_port_is_a(plugin, port, class_1)) {
+ bool matches = true;
+ for (size_t j = 0; j < n_classes; ++j) {
+ if (!lilv_port_is_a(plugin, port, classes[j])) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (matches) {
+ ++count;
+ }
+ }
+ }
+
+ free(classes);
+ return count;
+}
+
+LILV_API uint32_t
+lilv_plugin_get_num_ports_of_class(const LilvPlugin* plugin,
+ const LilvNode* class_1, ...)
+{
+ va_list args;
+ va_start(args, class_1);
+
+ uint32_t count = lilv_plugin_get_num_ports_of_class_va(plugin, class_1, args);
+
+ va_end(args);
+ return count;
+}
+
+LILV_API bool
+lilv_plugin_has_latency(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ SordIter* ports = lilv_world_query_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_port,
+ NULL);
+
+ bool ret = false;
+ FOREACH_MATCH(ports) {
+ const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
+ SordIter* prop = lilv_world_query_internal(
+ plugin->world,
+ port,
+ plugin->world->uris.lv2_portProperty,
+ plugin->world->uris.lv2_reportsLatency);
+ SordIter* des = lilv_world_query_internal(
+ plugin->world,
+ port,
+ plugin->world->uris.lv2_designation,
+ plugin->world->uris.lv2_latency);
+ const bool latent = !sord_iter_end(prop) || !sord_iter_end(des);
+ sord_iter_free(prop);
+ sord_iter_free(des);
+ if (latent) {
+ ret = true;
+ break;
+ }
+ }
+ sord_iter_free(ports);
+
+ return ret;
+}
+
+static const LilvPort*
+lilv_plugin_get_port_by_property(const LilvPlugin* plugin,
+ const SordNode* port_property)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ SordIter* iter = lilv_world_query_internal(
+ plugin->world,
+ port->node->node,
+ plugin->world->uris.lv2_portProperty,
+ port_property);
+
+ const bool found = !sord_iter_end(iter);
+ sord_iter_free(iter);
+
+ if (found) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_designation(const LilvPlugin* plugin,
+ const LilvNode* port_class,
+ const LilvNode* designation)
+{
+ LilvWorld* world = plugin->world;
+ lilv_plugin_load_ports_if_necessary(plugin);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ SordIter* iter = lilv_world_query_internal(
+ world,
+ port->node->node,
+ world->uris.lv2_designation,
+ designation->node);
+
+ const bool found = !sord_iter_end(iter) &&
+ (!port_class || lilv_port_is_a(plugin, port, port_class));
+ sord_iter_free(iter);
+
+ if (found) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+LILV_API uint32_t
+lilv_plugin_get_latency_port_index(const LilvPlugin* plugin)
+{
+ const LilvPort* prop_port = lilv_plugin_get_port_by_property(
+ plugin, plugin->world->uris.lv2_reportsLatency);
+ const LilvPort* des_port = lilv_plugin_get_port_by_property(
+ plugin, plugin->world->uris.lv2_latency);
+ if (prop_port) {
+ return prop_port->index;
+ } else if (des_port) {
+ return des_port->index;
+ } else {
+ return (uint32_t)-1;
+ }
+}
+
+LILV_API bool
+lilv_plugin_has_feature(const LilvPlugin* plugin,
+ const LilvNode* feature)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ const SordNode* predicates[] = { plugin->world->uris.lv2_requiredFeature,
+ plugin->world->uris.lv2_optionalFeature,
+ NULL };
+
+ for (const SordNode** pred = predicates; *pred; ++pred) {
+ if (lilv_world_ask_internal(
+ plugin->world, plugin->plugin_uri->node, *pred, feature->node)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_supported_features(const LilvPlugin* plugin)
+{
+ LilvNodes* optional = lilv_plugin_get_optional_features(plugin);
+ LilvNodes* required = lilv_plugin_get_required_features(plugin);
+ LilvNodes* result = lilv_nodes_merge(optional, required);
+ lilv_nodes_free(optional);
+ lilv_nodes_free(required);
+ return result;
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_optional_features(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_optionalFeature,
+ NULL);
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_required_features(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_find_nodes_internal(plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_requiredFeature,
+ NULL);
+}
+
+LILV_API bool
+lilv_plugin_has_extension_data(const LilvPlugin* plugin,
+ const LilvNode* uri)
+{
+ if (!lilv_node_is_uri(uri)) {
+ LILV_ERRORF("Extension data `%s' is not a URI\n",
+ sord_node_get_string(uri->node));
+ return false;
+ }
+
+ lilv_plugin_load_if_necessary(plugin);
+ return lilv_world_ask_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ plugin->world->uris.lv2_extensionData,
+ uri->node);
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_extension_data(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_value_internal(plugin, plugin->world->uris.lv2_extensionData);
+}
+
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_index(const LilvPlugin* plugin,
+ uint32_t index)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ if (index < plugin->num_ports) {
+ return plugin->ports[index];
+ } else {
+ return NULL;
+ }
+}
+
+LILV_API const LilvPort*
+lilv_plugin_get_port_by_symbol(const LilvPlugin* plugin,
+ const LilvNode* symbol)
+{
+ lilv_plugin_load_ports_if_necessary(plugin);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ LilvPort* port = plugin->ports[i];
+ if (lilv_node_equals(port->symbol, symbol)) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_project(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ SordNode* lv2_project = sord_new_uri(plugin->world->world,
+ (const uint8_t*)LV2_CORE__project);
+
+ SordIter* projects = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ lv2_project,
+ NULL);
+
+ sord_node_free(plugin->world->world, lv2_project);
+
+ if (sord_iter_end(projects)) {
+ sord_iter_free(projects);
+ return NULL;
+ }
+
+ const SordNode* project = sord_iter_get_node(projects, SORD_OBJECT);
+
+ sord_iter_free(projects);
+ return lilv_node_new_from_node(plugin->world, project);
+}
+
+static const SordNode*
+lilv_plugin_get_author(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ SordNode* doap_maintainer = sord_new_uri(
+ plugin->world->world, NS_DOAP "maintainer");
+
+ SordIter* maintainers = lilv_world_query_internal(
+ plugin->world,
+ plugin->plugin_uri->node,
+ doap_maintainer,
+ NULL);
+
+ if (sord_iter_end(maintainers)) {
+ sord_iter_free(maintainers);
+
+ LilvNode* project = lilv_plugin_get_project(plugin);
+ if (!project) {
+ sord_node_free(plugin->world->world, doap_maintainer);
+ return NULL;
+ }
+
+ maintainers = lilv_world_query_internal(
+ plugin->world,
+ project->node,
+ doap_maintainer,
+ NULL);
+
+ lilv_node_free(project);
+ }
+
+ sord_node_free(plugin->world->world, doap_maintainer);
+
+ if (sord_iter_end(maintainers)) {
+ sord_iter_free(maintainers);
+ return NULL;
+ }
+
+ const SordNode* author = sord_iter_get_node(maintainers, SORD_OBJECT);
+
+ sord_iter_free(maintainers);
+ return author;
+}
+
+static LilvNode*
+lilv_plugin_get_author_property(const LilvPlugin* plugin, const uint8_t* uri)
+{
+ const SordNode* author = lilv_plugin_get_author(plugin);
+ if (author) {
+ SordWorld* sworld = plugin->world->world;
+ SordNode* pred = sord_new_uri(sworld, uri);
+ LilvNode* ret = lilv_plugin_get_one(plugin, author, pred);
+ sord_node_free(sworld, pred);
+ return ret;
+ }
+ return NULL;
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_author_name(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_author_property(plugin, NS_FOAF "name");
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_author_email(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_author_property(plugin, NS_FOAF "mbox");
+}
+
+LILV_API LilvNode*
+lilv_plugin_get_author_homepage(const LilvPlugin* plugin)
+{
+ return lilv_plugin_get_author_property(plugin, NS_FOAF "homepage");
+}
+
+LILV_API bool
+lilv_plugin_is_replaced(const LilvPlugin* plugin)
+{
+ return plugin->replaced;
+}
+
+LILV_API LilvUIs*
+lilv_plugin_get_uis(const LilvPlugin* plugin)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ SordNode* ui_ui_node = sord_new_uri(plugin->world->world,
+ (const uint8_t*)LV2_UI__ui);
+ SordNode* ui_binary_node = sord_new_uri(plugin->world->world,
+ (const uint8_t*)LV2_UI__binary);
+
+ LilvUIs* result = lilv_uis_new();
+ SordIter* uis = lilv_world_query_internal(plugin->world,
+ plugin->plugin_uri->node,
+ ui_ui_node,
+ NULL);
+
+ FOREACH_MATCH(uis) {
+ const SordNode* ui = sord_iter_get_node(uis, SORD_OBJECT);
+
+ LilvNode* type = lilv_plugin_get_unique(plugin, ui, plugin->world->uris.rdf_a);
+ LilvNode* binary = lilv_plugin_get_one(plugin, ui, plugin->world->uris.lv2_binary);
+ if (!binary) {
+ binary = lilv_plugin_get_unique(plugin, ui, ui_binary_node);
+ }
+
+ if (sord_node_get_type(ui) != SORD_URI
+ || !lilv_node_is_uri(type)
+ || !lilv_node_is_uri(binary)) {
+ lilv_node_free(binary);
+ lilv_node_free(type);
+ LILV_ERRORF("Corrupt UI <%s>\n", sord_node_get_string(ui));
+ continue;
+ }
+
+ LilvUI* lilv_ui = lilv_ui_new(
+ plugin->world,
+ lilv_node_new_from_node(plugin->world, ui),
+ type,
+ binary);
+
+ zix_tree_insert((ZixTree*)result, lilv_ui, NULL);
+ }
+ sord_iter_free(uis);
+
+ sord_node_free(plugin->world->world, ui_binary_node);
+ sord_node_free(plugin->world->world, ui_ui_node);
+
+ if (lilv_uis_size(result) > 0) {
+ return result;
+ } else {
+ lilv_uis_free(result);
+ return NULL;
+ }
+}
+
+LILV_API LilvNodes*
+lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type)
+{
+ lilv_plugin_load_if_necessary(plugin);
+
+ LilvWorld* const world = plugin->world;
+ LilvNodes* const related = lilv_world_find_nodes_internal(
+ world,
+ NULL,
+ world->uris.lv2_appliesTo,
+ lilv_plugin_get_uri(plugin)->node);
+
+ if (!type) {
+ return related;
+ }
+
+ LilvNodes* matches = lilv_nodes_new();
+ LILV_FOREACH(nodes, i, related) {
+ LilvNode* node = (LilvNode*)lilv_collection_get((ZixTree*)related, i);
+ if (lilv_world_ask_internal(
+ world, node->node, world->uris.rdf_a, type->node)) {
+ zix_tree_insert((ZixTree*)matches,
+ lilv_node_new_from_node(world, node->node),
+ NULL);
+ }
+ }
+
+ lilv_nodes_free(related);
+ return matches;
+}
+
+static SerdEnv*
+new_lv2_env(const SerdNode* base)
+{
+ SerdEnv* env = serd_env_new(base);
+
+#define USTR(s) ((const uint8_t*)(s))
+ serd_env_set_prefix_from_strings(env, USTR("doap"), USTR(LILV_NS_DOAP));
+ serd_env_set_prefix_from_strings(env, USTR("foaf"), USTR(LILV_NS_FOAF));
+ serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2));
+ serd_env_set_prefix_from_strings(env, USTR("owl"), USTR(LILV_NS_OWL));
+ serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF));
+ serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
+ serd_env_set_prefix_from_strings(env, USTR("xsd"), USTR(LILV_NS_XSD));
+
+ return env;
+}
+
+static void
+maybe_write_prefixes(SerdWriter* writer, SerdEnv* env, FILE* file)
+{
+ fseek(file, 0, SEEK_END);
+ if (ftell(file) == 0) {
+ serd_env_foreach(
+ env, (SerdPrefixSink)serd_writer_set_prefix, writer);
+ } else {
+ fprintf(file, "\n");
+ }
+}
+
+LILV_API void
+lilv_plugin_write_description(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* plugin_file)
+{
+ const LilvNode* subject = lilv_plugin_get_uri(plugin);
+ const uint32_t num_ports = lilv_plugin_get_num_ports(plugin);
+ const SerdNode* base = sord_node_to_serd_node(base_uri->node);
+ SerdEnv* env = new_lv2_env(base);
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ (SerdStyle)(SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
+ env,
+ NULL,
+ serd_file_sink,
+ plugin_file);
+
+ // Write prefixes if this is a new file
+ maybe_write_prefixes(writer, env, plugin_file);
+
+ // Write plugin description
+ SordIter* plug_iter = lilv_world_query_internal(
+ world, subject->node, NULL, NULL);
+ sord_write_iter(plug_iter, writer);
+
+ // Write port descriptions
+ for (uint32_t i = 0; i < num_ports; ++i) {
+ const LilvPort* port = plugin->ports[i];
+ SordIter* port_iter = lilv_world_query_internal(
+ world, port->node->node, NULL, NULL);
+ sord_write_iter(port_iter, writer);
+ }
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
+
+LILV_API void
+lilv_plugin_write_manifest_entry(LilvWorld* world,
+ const LilvPlugin* plugin,
+ const LilvNode* base_uri,
+ FILE* manifest_file,
+ const char* plugin_file_path)
+{
+ const LilvNode* subject = lilv_plugin_get_uri(plugin);
+ const SerdNode* base = sord_node_to_serd_node(base_uri->node);
+ SerdEnv* env = new_lv2_env(base);
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ (SerdStyle)(SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
+ env,
+ NULL,
+ serd_file_sink,
+ manifest_file);
+
+ // Write prefixes if this is a new file
+ maybe_write_prefixes(writer, env, manifest_file);
+
+ // Write manifest entry
+ serd_writer_write_statement(
+ writer, 0, NULL,
+ sord_node_to_serd_node(subject->node),
+ sord_node_to_serd_node(plugin->world->uris.rdf_a),
+ sord_node_to_serd_node(plugin->world->uris.lv2_Plugin), 0, 0);
+
+ const SerdNode file_node = serd_node_from_string(
+ SERD_URI, (const uint8_t*)plugin_file_path);
+ serd_writer_write_statement(
+ writer, 0, NULL,
+ sord_node_to_serd_node(subject->node),
+ sord_node_to_serd_node(plugin->world->uris.rdfs_seeAlso),
+ &file_node, 0, 0);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+}
diff --git a/src/pluginclass.c b/src/pluginclass.c
new file mode 100644
index 0000000..622ff8f
--- /dev/null
+++ b/src/pluginclass.c
@@ -0,0 +1,88 @@
+/*
+ Copyright 2007-2015 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 <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)
+{
+ 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)
+{
+ if (!plugin_class) {
+ return;
+ }
+
+ 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)
+{
+ return plugin_class->parent_uri ? plugin_class->parent_uri : NULL;
+}
+
+LILV_API const LilvNode*
+lilv_plugin_class_get_uri(const LilvPluginClass* plugin_class)
+{
+ 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;
+}
diff --git a/src/port.c b/src/port.c
new file mode 100644
index 0000000..fcc32fc
--- /dev/null
+++ b/src/port.c
@@ -0,0 +1,268 @@
+/*
+ Copyright 2007-2014 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/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/event/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 = lilv_node_new_from_node(world, 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) {
+ lilv_node_free(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* plugin,
+ const LilvPort* port,
+ const LilvNode* property)
+{
+ return lilv_world_ask_internal(plugin->world,
+ port->node->node,
+ plugin->world->uris.lv2_portProperty,
+ property->node);
+}
+
+LILV_API bool
+lilv_port_supports_event(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* event_type)
+{
+ const uint8_t* predicates[] = { (const uint8_t*)LV2_EVENT__supportsEvent,
+ (const uint8_t*)LV2_ATOM__supports,
+ NULL };
+
+ for (const uint8_t** pred = predicates; *pred; ++pred) {
+ if (lilv_world_ask_internal(plugin->world,
+ port->node->node,
+ sord_new_uri(plugin->world->world, *pred),
+ event_type->node)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static LilvNodes*
+lilv_port_get_value_by_node(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const SordNode* predicate)
+{
+ return lilv_world_find_nodes_internal(plugin->world,
+ port->node->node,
+ predicate,
+ NULL);
+}
+
+LILV_API const LilvNode*
+lilv_port_get_node(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->node;
+}
+
+LILV_API LilvNodes*
+lilv_port_get_value(const LilvPlugin* plugin,
+ 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(plugin, port, predicate->node);
+}
+
+LILV_API LilvNode*
+lilv_port_get(const LilvPlugin* plugin,
+ const LilvPort* port,
+ const LilvNode* predicate)
+{
+ LilvNodes* values = lilv_port_get_value(plugin, port, predicate);
+
+ LilvNode* value = lilv_node_duplicate(
+ values ? lilv_nodes_get_first(values) : NULL);
+
+ lilv_nodes_free(values);
+ return value;
+}
+
+LILV_API uint32_t
+lilv_port_get_index(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->index;
+}
+
+LILV_API const LilvNode*
+lilv_port_get_symbol(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->symbol;
+}
+
+LILV_API LilvNode*
+lilv_port_get_name(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ LilvNodes* results = lilv_port_get_value_by_node(
+ plugin, port, plugin->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(plugin)));
+ }
+
+ return ret;
+}
+
+LILV_API const LilvNodes*
+lilv_port_get_classes(const LilvPlugin* plugin,
+ const LilvPort* port)
+{
+ return port->classes;
+}
+
+LILV_API void
+lilv_port_get_range(const LilvPlugin* plugin,
+ const LilvPort* port,
+ LilvNode** def,
+ LilvNode** min,
+ LilvNode** max)
+{
+ if (def) {
+ LilvNodes* defaults = lilv_port_get_value_by_node(
+ plugin, port, plugin->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(
+ plugin, port, plugin->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(
+ plugin, port, plugin->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* plugin,
+ const LilvPort* port)
+{
+ SordIter* points = lilv_world_query_internal(
+ plugin->world,
+ port->node->node,
+ sord_new_uri(plugin->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(plugin,
+ point,
+ plugin->world->uris.rdf_value);
+
+ LilvNode* label = lilv_plugin_get_unique(plugin,
+ point,
+ plugin->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* plugin,
+ const LilvPort* port)
+{
+ LilvNode* pred = lilv_node_new_from_node(
+ plugin->world, plugin->world->uris.lv2_portProperty);
+ LilvNodes* ret = lilv_port_get_value(plugin, port, pred);
+ lilv_node_free(pred);
+ return ret;
+}
diff --git a/src/query.c b/src/query.c
new file mode 100644
index 0000000..fe2988f
--- /dev/null
+++ b/src/query.c
@@ -0,0 +1,139 @@
+/*
+ Copyright 2007-2015 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 <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;
+ }
+}
diff --git a/src/scalepoint.c b/src/scalepoint.c
new file mode 100644
index 0000000..349b2b3
--- /dev/null
+++ b/src/scalepoint.c
@@ -0,0 +1,49 @@
+/*
+ Copyright 2007-2014 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(LilvScalePoint));
+ 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* point)
+{
+ return point->value;
+}
+
+LILV_API const LilvNode*
+lilv_scale_point_get_label(const LilvScalePoint* point)
+{
+ return point->label;
+}
diff --git a/src/state.c b/src/state.c
new file mode 100644
index 0000000..2d1b349
--- /dev/null
+++ b/src/state.c
@@ -0,0 +1,1329 @@
+/*
+ Copyright 2007-2017 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+
+#include "lilv_config.h"
+#include "lilv_internal.h"
+#include "sratom/sratom.h"
+
+#define USTR(s) ((const uint8_t*)(s))
+
+typedef struct {
+ void* value; ///< Value/Object
+ size_t size; ///< Size of value
+ uint32_t key; ///< Key/Predicate (URID)
+ uint32_t type; ///< Type of value (URID)
+ uint32_t flags; ///< State flags (POD, etc)
+} Property;
+
+typedef struct {
+ char* symbol; ///< Symbol of port
+ void* value; ///< Value of port
+ uint32_t size; ///< Size of value
+ uint32_t type; ///< Type of value (URID)
+} PortValue;
+
+typedef struct {
+ char* abs; ///< Absolute path of actual file
+ char* rel; ///< Abstract path (relative path in state dir)
+} PathMap;
+
+typedef struct {
+ size_t n;
+ Property* props;
+} PropertyArray;
+
+struct LilvStateImpl {
+ LilvNode* plugin_uri; ///< Plugin URI
+ LilvNode* uri; ///< State/preset URI
+ char* dir; ///< Save directory (if saved)
+ char* file_dir; ///< Directory for files created by plugin
+ char* copy_dir; ///< Directory for snapshots of external files
+ char* link_dir; ///< Directory for links to external files
+ char* label; ///< State/Preset label
+ ZixTree* abs2rel; ///< PathMap sorted by abs
+ ZixTree* rel2abs; ///< PathMap sorted by rel
+ PropertyArray props; ///< State properties
+ PropertyArray metadata; ///< State metadata
+ PortValue* values; ///< Port values
+ uint32_t atom_Path; ///< atom:Path URID
+ uint32_t n_values; ///< Number of port values
+};
+
+static int
+abs_cmp(const void* a, const void* b, void* user_data)
+{
+ return strcmp(((const PathMap*)a)->abs, ((const PathMap*)b)->abs);
+}
+
+static int
+rel_cmp(const void* a, const void* b, void* user_data)
+{
+ return strcmp(((const PathMap*)a)->rel, ((const PathMap*)b)->rel);
+}
+
+static int
+property_cmp(const void* a, const void* b)
+{
+ return ((const Property*)a)->key - ((const Property*)b)->key;
+}
+
+static int
+value_cmp(const void* a, const void* b)
+{
+ return strcmp(((const PortValue*)a)->symbol,
+ ((const PortValue*)b)->symbol);
+}
+
+static void
+path_rel_free(void* ptr)
+{
+ free(((PathMap*)ptr)->abs);
+ free(((PathMap*)ptr)->rel);
+ free(ptr);
+}
+
+static PortValue*
+append_port_value(LilvState* state,
+ const char* port_symbol,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ PortValue* pv = NULL;
+ if (value) {
+ state->values = (PortValue*)realloc(
+ state->values, (++state->n_values) * sizeof(PortValue));
+ pv = &state->values[state->n_values - 1];
+ pv->symbol = lilv_strdup(port_symbol);
+ pv->value = malloc(size);
+ pv->size = size;
+ pv->type = type;
+ memcpy(pv->value, value, size);
+ }
+ return pv;
+}
+
+static const char*
+lilv_state_rel2abs(const LilvState* state, const char* path)
+{
+ ZixTreeIter* iter = NULL;
+ const PathMap key = { NULL, (char*)path };
+ if (state->rel2abs && !zix_tree_find(state->rel2abs, &key, &iter)) {
+ return ((const PathMap*)zix_tree_get(iter))->abs;
+ }
+ return path;
+}
+
+static void
+append_property(LilvState* state,
+ PropertyArray* array,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags)
+{
+ array->props = (Property*)realloc(
+ array->props, (++array->n) * sizeof(Property));
+
+ Property* const prop = &array->props[array->n - 1];
+ if ((flags & LV2_STATE_IS_POD) || type == state->atom_Path) {
+ prop->value = malloc(size);
+ memcpy(prop->value, value, size);
+ } else {
+ prop->value = (void*)value;
+ }
+
+ prop->size = size;
+ prop->key = key;
+ prop->type = type;
+ prop->flags = flags;
+}
+
+static LV2_State_Status
+store_callback(LV2_State_Handle handle,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags)
+{
+ LilvState* const state = (LilvState*)handle;
+ append_property(state, &state->props, key, value, size, type, flags);
+ return LV2_STATE_SUCCESS;
+}
+
+static const void*
+retrieve_callback(LV2_State_Handle handle,
+ uint32_t key,
+ size_t* size,
+ uint32_t* type,
+ uint32_t* flags)
+{
+ const LilvState* const state = (LilvState*)handle;
+ const Property search_key = { NULL, 0, key, 0, 0 };
+ const Property* const prop = (Property*)bsearch(
+ &search_key, state->props.props, state->props.n,
+ sizeof(Property), property_cmp);
+
+ if (prop) {
+ *size = prop->size;
+ *type = prop->type;
+ *flags = prop->flags;
+ return prop->value;
+ }
+ return NULL;
+}
+
+static bool
+lilv_state_has_path(const char* path, void* state)
+{
+ return lilv_state_rel2abs((LilvState*)state, path) != path;
+}
+
+static char*
+make_path(LV2_State_Make_Path_Handle handle, const char* path)
+{
+ LilvState* state = (LilvState*)handle;
+ lilv_mkdir_p(state->dir);
+
+ return lilv_path_join(state->dir, path);
+}
+
+static char*
+abstract_path(LV2_State_Map_Path_Handle handle,
+ const char* abs_path)
+{
+ LilvState* state = (LilvState*)handle;
+ char* path = NULL;
+ char* real_path = lilv_realpath(abs_path);
+ const PathMap key = { real_path, NULL };
+ ZixTreeIter* iter = NULL;
+
+ if (abs_path[0] == '\0') {
+ return lilv_strdup(abs_path);
+ } else if (!zix_tree_find(state->abs2rel, &key, &iter)) {
+ // Already mapped path in a previous call
+ PathMap* pm = (PathMap*)zix_tree_get(iter);
+ free(real_path);
+ return lilv_strdup(pm->rel);
+ } else if (lilv_path_is_child(real_path, state->dir)) {
+ // File in state directory (loaded, or created by plugin during save)
+ path = lilv_path_relative_to(real_path, state->dir);
+ } else if (lilv_path_is_child(real_path, state->file_dir)) {
+ // File created by plugin earlier
+ path = lilv_path_relative_to(real_path, state->file_dir);
+ if (state->copy_dir) {
+ int st = lilv_mkdir_p(state->copy_dir);
+ if (st) {
+ LILV_ERRORF("Error creating directory %s (%s)\n",
+ state->copy_dir, strerror(st));
+ }
+
+ char* cpath = lilv_path_join(state->copy_dir, path);
+ char* copy = lilv_get_latest_copy(real_path, cpath);
+ if (!copy || !lilv_file_equals(real_path, copy)) {
+ // No recent enough copy, make a new one
+ free(copy);
+ copy = lilv_find_free_path(cpath, lilv_path_exists, NULL);
+ if ((st = lilv_copy_file(real_path, copy))) {
+ LILV_ERRORF("Error copying state file %s (%s)\n",
+ copy, strerror(st));
+ }
+ }
+ free(real_path);
+ free(cpath);
+
+ // Refer to the latest copy in plugin state
+ real_path = copy;
+ }
+ } else if (state->link_dir) {
+ // New path outside state directory, make a link
+ const char* slash = strrchr(real_path, '/');
+ const char* name = slash ? (slash + 1) : real_path;
+
+ // Find a free name in the (virtual) state directory
+ path = lilv_find_free_path(name, lilv_state_has_path, state);
+ } else {
+ // No link directory, preserve absolute path
+ path = lilv_strdup(abs_path);
+ }
+
+ // Add record to path mapping
+ PathMap* pm = (PathMap*)malloc(sizeof(PathMap));
+ pm->abs = real_path;
+ pm->rel = lilv_strdup(path);
+ zix_tree_insert(state->abs2rel, pm, NULL);
+ zix_tree_insert(state->rel2abs, pm, NULL);
+
+ return path;
+}
+
+static char*
+absolute_path(LV2_State_Map_Path_Handle handle,
+ const char* state_path)
+{
+ LilvState* state = (LilvState*)handle;
+ char* path = NULL;
+ if (lilv_path_is_absolute(state_path)) {
+ // Absolute path, return identical path
+ path = lilv_strdup(state_path);
+ } else if (state->dir) {
+ // Relative path inside state directory
+ path = lilv_path_join(state->dir, state_path);
+ } else {
+ // State has not been saved, unmap
+ path = lilv_strdup(lilv_state_rel2abs(state, state_path));
+ }
+
+ return path;
+}
+
+/** Return a new features array which is `feature` added to `features`. */
+static const LV2_Feature**
+add_features(const LV2_Feature *const * features,
+ const LV2_Feature* map, const LV2_Feature* make)
+{
+ size_t n_features = 0;
+ for (; features && features[n_features]; ++n_features) {}
+
+ const LV2_Feature** ret = (const LV2_Feature**)calloc(
+ n_features + 3, sizeof(LV2_Feature*));
+
+ if (features) {
+ memcpy(ret, features, n_features * sizeof(LV2_Feature*));
+ }
+
+ ret[n_features] = map;
+ ret[n_features + 1] = make;
+ return ret;
+}
+
+static char*
+absolute_dir(const char* path)
+{
+ char* abs_path = lilv_path_absolute(path);
+ char* base = lilv_path_join(abs_path, NULL);
+ free(abs_path);
+ return base;
+}
+
+static const char*
+state_strerror(LV2_State_Status st)
+{
+ switch (st) {
+ case LV2_STATE_SUCCESS: return "Completed successfully";
+ case LV2_STATE_ERR_BAD_TYPE: return "Unsupported type";
+ case LV2_STATE_ERR_BAD_FLAGS: return "Unsupported flags";
+ case LV2_STATE_ERR_NO_FEATURE: return "Missing features";
+ case LV2_STATE_ERR_NO_PROPERTY: return "Missing property";
+ default: return "Unknown error";
+ }
+}
+
+LILV_API LilvState*
+lilv_state_new_from_instance(const LilvPlugin* plugin,
+ LilvInstance* instance,
+ LV2_URID_Map* map,
+ const char* file_dir,
+ const char* copy_dir,
+ const char* link_dir,
+ const char* save_dir,
+ LilvGetPortValueFunc get_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features)
+{
+ const LV2_Feature** sfeatures = NULL;
+ LilvWorld* const world = plugin->world;
+ LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState));
+ state->plugin_uri = lilv_node_duplicate(lilv_plugin_get_uri(plugin));
+ state->abs2rel = zix_tree_new(false, abs_cmp, NULL, path_rel_free);
+ state->rel2abs = zix_tree_new(false, rel_cmp, NULL, NULL);
+ state->file_dir = file_dir ? absolute_dir(file_dir) : NULL;
+ state->copy_dir = copy_dir ? absolute_dir(copy_dir) : NULL;
+ state->link_dir = link_dir ? absolute_dir(link_dir) : NULL;
+ state->dir = save_dir ? absolute_dir(save_dir) : NULL;
+ state->atom_Path = map->map(map->handle, LV2_ATOM__Path);
+
+ LV2_State_Map_Path pmap = { state, abstract_path, absolute_path };
+ LV2_Feature pmap_feature = { LV2_STATE__mapPath, &pmap };
+ LV2_State_Make_Path pmake = { state, make_path };
+ LV2_Feature pmake_feature = { LV2_STATE__makePath, &pmake };
+ features = sfeatures = add_features(features, &pmap_feature,
+ save_dir ? &pmake_feature : NULL);
+
+ // Store port values
+ if (get_value) {
+ LilvNode* lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
+ LilvNode* lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT);
+ for (uint32_t i = 0; i < plugin->num_ports; ++i) {
+ const LilvPort* const port = plugin->ports[i];
+ if (lilv_port_is_a(plugin, port, lv2_ControlPort)
+ && lilv_port_is_a(plugin, port, lv2_InputPort)) {
+ uint32_t size, type;
+ const char* sym = lilv_node_as_string(port->symbol);
+ const void* value = get_value(sym, user_data, &size, &type);
+ append_port_value(state, sym, value, size, type);
+ }
+ }
+ lilv_node_free(lv2_ControlPort);
+ lilv_node_free(lv2_InputPort);
+ }
+
+ // Store properties
+ const LV2_Descriptor* desc = instance->lv2_descriptor;
+ const LV2_State_Interface* iface = (desc->extension_data)
+ ? (const LV2_State_Interface*)desc->extension_data(LV2_STATE__interface)
+ : NULL;
+
+ if (iface) {
+ LV2_State_Status st = iface->save(
+ instance->lv2_handle, store_callback, state, flags, features);
+ if (st) {
+ LILV_ERRORF("Error saving plugin state: %s\n", state_strerror(st));
+ free(state->props.props);
+ state->props.props = NULL;
+ state->props.n = 0;
+ } else {
+ qsort(state->props.props, state->props.n, sizeof(Property), property_cmp);
+ }
+ }
+
+ qsort(state->values, state->n_values, sizeof(PortValue), value_cmp);
+
+ free(sfeatures);
+ return state;
+}
+
+LILV_API void
+lilv_state_emit_port_values(const LilvState* state,
+ LilvSetPortValueFunc set_value,
+ void* user_data)
+{
+ for (uint32_t i = 0; i < state->n_values; ++i) {
+ const PortValue* val = &state->values[i];
+ set_value(val->symbol, user_data, val->value, val->size, val->type);
+ }
+}
+
+LILV_API void
+lilv_state_restore(const LilvState* state,
+ LilvInstance* instance,
+ LilvSetPortValueFunc set_value,
+ void* user_data,
+ uint32_t flags,
+ const LV2_Feature *const * features)
+{
+ if (!state) {
+ LILV_ERROR("lilv_state_restore() called on NULL state\n");
+ return;
+ }
+
+ LV2_State_Map_Path map_path = {
+ (LilvState*)state, abstract_path, absolute_path };
+ LV2_Feature map_feature = { LV2_STATE__mapPath, &map_path };
+
+ if (instance) {
+ const LV2_Descriptor* desc = instance->lv2_descriptor;
+ if (desc->extension_data) {
+ const LV2_State_Interface* iface = (const LV2_State_Interface*)
+ desc->extension_data(LV2_STATE__interface);
+
+ if (iface && iface->restore) {
+ const LV2_Feature** sfeatures = add_features(
+ features, &map_feature, NULL);
+
+ iface->restore(instance->lv2_handle, retrieve_callback,
+ (LV2_State_Handle)state, flags, sfeatures);
+
+ free(sfeatures);
+ }
+ }
+ }
+
+
+ if (set_value) {
+ lilv_state_emit_port_values(state, set_value, user_data);
+ }
+}
+
+static LilvState*
+new_state_from_model(LilvWorld* world,
+ LV2_URID_Map* map,
+ SordModel* model,
+ const SordNode* node,
+ const char* dir)
+{
+ // Check that we know at least something about this state subject
+ if (!sord_ask(model, node, 0, 0, 0)) {
+ return NULL;
+ }
+
+ // Allocate state
+ LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState));
+ state->dir = lilv_strdup(dir);
+ state->atom_Path = map->map(map->handle, LV2_ATOM__Path);
+ state->uri = lilv_node_new_from_node(world, node);
+
+ // Get the plugin URI this state applies to
+ SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0);
+ if (i) {
+ const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
+ const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH);
+ state->plugin_uri = lilv_node_new_from_node(world, object);
+ if (!state->dir && graph) {
+ state->dir = lilv_strdup((const char*)sord_node_get_string(graph));
+ }
+ sord_iter_free(i);
+ } else if (sord_ask(model,
+ node,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin, 0)) {
+ // Loading plugin description as state (default state)
+ state->plugin_uri = lilv_node_new_from_node(world, node);
+ } else {
+ LILV_ERRORF("State %s missing lv2:appliesTo property\n",
+ sord_node_get_string(node));
+ }
+
+ // Get the state label
+ i = sord_search(model, node, world->uris.rdfs_label, NULL, NULL);
+ if (i) {
+ const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
+ const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH);
+ state->label = lilv_strdup((const char*)sord_node_get_string(object));
+ if (!state->dir && graph) {
+ state->dir = lilv_strdup((const char*)sord_node_get_string(graph));
+ }
+ sord_iter_free(i);
+ }
+
+ Sratom* sratom = sratom_new(map);
+ SerdChunk chunk = { NULL, 0 };
+ LV2_Atom_Forge forge;
+ lv2_atom_forge_init(&forge, map);
+ lv2_atom_forge_set_sink(
+ &forge, sratom_forge_sink, sratom_forge_deref, &chunk);
+
+ // Get port values
+ SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0);
+ FOREACH_MATCH(ports) {
+ const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
+
+ SordNode* label = sord_get(model, port, world->uris.rdfs_label, 0, 0);
+ SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0);
+ SordNode* value = sord_get(model, port, world->uris.pset_value, 0, 0);
+ if (!value) {
+ value = sord_get(model, port, world->uris.lv2_default, 0, 0);
+ }
+ if (!symbol) {
+ LILV_ERRORF("State `%s' port missing symbol.\n",
+ sord_node_get_string(node));
+ } else if (value) {
+ chunk.len = 0;
+ sratom_read(sratom, &forge, world->world, model, value);
+ const LV2_Atom* atom = (const LV2_Atom*)chunk.buf;
+
+ append_port_value(state,
+ (const char*)sord_node_get_string(symbol),
+ LV2_ATOM_BODY_CONST(atom),
+ atom->size, atom->type);
+
+ if (label) {
+ lilv_state_set_label(state,
+ (const char*)sord_node_get_string(label));
+ }
+ }
+ sord_node_free(world->world, value);
+ sord_node_free(world->world, symbol);
+ sord_node_free(world->world, label);
+ }
+ sord_iter_free(ports);
+
+ // Get properties
+ SordNode* statep = sord_new_uri(world->world, USTR(LV2_STATE__state));
+ SordNode* state_node = sord_get(model, node, statep, NULL, NULL);
+ if (state_node) {
+ SordIter* props = sord_search(model, state_node, 0, 0, 0);
+ FOREACH_MATCH(props) {
+ const SordNode* p = sord_iter_get_node(props, SORD_PREDICATE);
+ const SordNode* o = sord_iter_get_node(props, SORD_OBJECT);
+ const char* key = (const char*)sord_node_get_string(p);
+
+ chunk.len = 0;
+ lv2_atom_forge_set_sink(
+ &forge, sratom_forge_sink, sratom_forge_deref, &chunk);
+
+ sratom_read(sratom, &forge, world->world, model, o);
+ const LV2_Atom* atom = (const LV2_Atom*)chunk.buf;
+ uint32_t flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
+ Property prop = { NULL, 0, 0, 0, flags };
+
+ prop.key = map->map(map->handle, key);
+ prop.type = atom->type;
+ prop.size = atom->size;
+ prop.value = malloc(atom->size);
+ memcpy(prop.value, LV2_ATOM_BODY_CONST(atom), atom->size);
+ if (atom->type == forge.Path) {
+ prop.flags = LV2_STATE_IS_POD;
+ }
+
+ if (prop.value) {
+ state->props.props = (Property*)realloc(
+ state->props.props, (++state->props.n) * sizeof(Property));
+ state->props.props[state->props.n - 1] = prop;
+ }
+ }
+ sord_iter_free(props);
+ }
+ sord_node_free(world->world, state_node);
+ sord_node_free(world->world, statep);
+
+ free((void*)chunk.buf);
+ sratom_free(sratom);
+
+ if (state->props.props) {
+ qsort(state->props.props, state->props.n, sizeof(Property), property_cmp);
+ }
+ if (state->values) {
+ qsort(state->values, state->n_values, sizeof(PortValue), value_cmp);
+ }
+
+ return state;
+}
+
+LILV_API LilvState*
+lilv_state_new_from_world(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* node)
+{
+ if (!lilv_node_is_uri(node) && !lilv_node_is_blank(node)) {
+ LILV_ERRORF("Subject `%s' is not a URI or blank node.\n",
+ lilv_node_as_string(node));
+ return NULL;
+ }
+
+ return new_state_from_model(world, map, world->model, node->node, NULL);
+}
+
+LILV_API LilvState*
+lilv_state_new_from_file(LilvWorld* world,
+ LV2_URID_Map* map,
+ const LilvNode* subject,
+ const char* path)
+{
+ if (subject && !lilv_node_is_uri(subject)
+ && !lilv_node_is_blank(subject)) {
+ LILV_ERRORF("Subject `%s' is not a URI or blank node.\n",
+ lilv_node_as_string(subject));
+ return NULL;
+ }
+
+ uint8_t* abs_path = (uint8_t*)lilv_path_absolute(path);
+ SerdNode node = serd_node_new_file_uri(abs_path, NULL, NULL, true);
+ SerdEnv* env = serd_env_new(&node);
+ SordModel* model = sord_new(world->world, SORD_SPO, false);
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ serd_reader_read_file(reader, node.buf);
+
+ SordNode* subject_node = (subject)
+ ? subject->node
+ : sord_node_from_serd_node(world->world, env, &node, NULL, NULL);
+
+ char* dirname = lilv_dirname(path);
+ char* real_path = lilv_realpath(dirname);
+ LilvState* state = new_state_from_model(
+ world, map, model, subject_node, real_path);
+ free(dirname);
+ free(real_path);
+
+ serd_node_free(&node);
+ free(abs_path);
+ serd_reader_free(reader);
+ sord_free(model);
+ serd_env_free(env);
+ return state;
+}
+
+static void
+set_prefixes(SerdEnv* env)
+{
+#define SET_PSET(e, p, u) serd_env_set_prefix_from_strings(e, p, u)
+ SET_PSET(env, USTR("atom"), USTR(LV2_ATOM_PREFIX));
+ SET_PSET(env, USTR("lv2"), USTR(LV2_CORE_PREFIX));
+ SET_PSET(env, USTR("pset"), USTR(LV2_PRESETS_PREFIX));
+ SET_PSET(env, USTR("rdf"), USTR(LILV_NS_RDF));
+ SET_PSET(env, USTR("rdfs"), USTR(LILV_NS_RDFS));
+ SET_PSET(env, USTR("state"), USTR(LV2_STATE_PREFIX));
+ SET_PSET(env, USTR("xsd"), USTR(LILV_NS_XSD));
+}
+
+LILV_API LilvState*
+lilv_state_new_from_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ const char* str)
+{
+ if (!str) {
+ return NULL;
+ }
+
+ SerdNode base = SERD_NODE_NULL;
+ SerdEnv* env = serd_env_new(&base);
+ SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false);
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ set_prefixes(env);
+ serd_reader_read_string(reader, USTR(str));
+
+ SordNode* o = sord_new_uri(world->world, USTR(LV2_PRESETS__Preset));
+ SordNode* s = sord_get(model, NULL, world->uris.rdf_a, o, NULL);
+
+ LilvState* state = new_state_from_model(world, map, model, s, NULL);
+
+ sord_node_free(world->world, s);
+ sord_node_free(world->world, o);
+ serd_reader_free(reader);
+ sord_free(model);
+ serd_env_free(env);
+
+ return state;
+}
+
+static SerdWriter*
+ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env)
+{
+ SerdURI base_uri = SERD_URI_NULL;
+ if (base && base->buf) {
+ serd_uri_parse(base->buf, &base_uri);
+ }
+
+ SerdEnv* env = *new_env ? *new_env : serd_env_new(base);
+ set_prefixes(env);
+
+ SerdWriter* writer = serd_writer_new(
+ SERD_TURTLE,
+ (SerdStyle)(SERD_STYLE_RESOLVED |
+ SERD_STYLE_ABBREVIATED|
+ SERD_STYLE_CURIED),
+ env,
+ &base_uri,
+ sink,
+ stream);
+
+ if (!*new_env) {
+ *new_env = env;
+ }
+
+ return writer;
+}
+
+static SerdWriter*
+ttl_file_writer(FILE* fd, const SerdNode* node, SerdEnv** env)
+{
+ SerdWriter* writer = ttl_writer(serd_file_sink, fd, node, env);
+
+ fseek(fd, 0, SEEK_END);
+ if (ftell(fd) == 0) {
+ serd_env_foreach(*env, (SerdPrefixSink)serd_writer_set_prefix, writer);
+ } else {
+ fprintf(fd, "\n");
+ }
+
+ return writer;
+}
+
+static void
+add_to_model(SordWorld* world,
+ SerdEnv* env,
+ SordModel* model,
+ const SerdNode s,
+ const SerdNode p,
+ const SerdNode o)
+{
+ SordNode* ss = sord_node_from_serd_node(world, env, &s, NULL, NULL);
+ SordNode* sp = sord_node_from_serd_node(world, env, &p, NULL, NULL);
+ SordNode* so = sord_node_from_serd_node(world, env, &o, NULL, NULL);
+
+ SordQuad quad = { ss, sp, so, NULL };
+ sord_add(model, quad);
+
+ sord_node_free(world, ss);
+ sord_node_free(world, sp);
+ sord_node_free(world, so);
+}
+
+static void
+remove_manifest_entry(SordWorld* world, SordModel* model, const char* subject)
+{
+ SordNode* s = sord_new_uri(world, USTR(subject));
+ SordIter* i = sord_search(model, s, NULL, NULL, NULL);
+ while (!sord_iter_end(i)) {
+ sord_erase(model, i);
+ }
+ sord_iter_free(i);
+ sord_node_free(world, s);
+}
+
+static int
+add_state_to_manifest(LilvWorld* lworld,
+ const LilvNode* plugin_uri,
+ const char* manifest_path,
+ const char* state_uri,
+ const char* state_path)
+{
+ SordWorld* world = lworld->world;
+ SerdNode manifest = serd_node_new_file_uri(USTR(manifest_path), 0, 0, 1);
+ SerdNode file = serd_node_new_file_uri(USTR(state_path), 0, 0, 1);
+ SerdEnv* env = serd_env_new(&manifest);
+ SordModel* model = sord_new(world, SORD_SPO, false);
+
+ FILE* rfd = fopen(manifest_path, "r");
+ if (rfd) {
+ // Read manifest into model
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+ lilv_flock(rfd, true);
+ serd_reader_read_file_handle(reader, rfd, manifest.buf);
+ serd_reader_free(reader);
+ }
+
+ // Choose state URI (use file URI if not given)
+ if (!state_uri) {
+ state_uri = (const char*)file.buf;
+ }
+
+ // Remove any existing manifest entries for this state
+ remove_manifest_entry(world, model, state_uri);
+
+ // Add manifest entry for this state to model
+ SerdNode s = serd_node_from_string(SERD_URI, USTR(state_uri));
+
+ // <state> a pset:Preset
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")),
+ serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)));
+
+ // <state> a pset:Preset
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")),
+ serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)));
+
+ // <state> rdfs:seeAlso <file>
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "seeAlso")),
+ file);
+
+ // <state> lv2:appliesTo <plugin>
+ add_to_model(world, env, model,
+ s,
+ serd_node_from_string(SERD_URI, USTR(LV2_CORE__appliesTo)),
+ serd_node_from_string(SERD_URI,
+ USTR(lilv_node_as_string(plugin_uri))));
+
+ // Write manifest model to file
+ FILE* wfd = fopen(manifest_path, "w");
+ if (wfd) {
+ SerdWriter* writer = ttl_file_writer(wfd, &manifest, &env);
+ sord_write(model, writer, NULL);
+ serd_writer_free(writer);
+ fclose(wfd);
+ } else {
+ LILV_ERRORF("Failed to open %s for writing (%s)\n",
+ manifest_path, strerror(errno));
+ }
+
+ sord_free(model);
+ serd_node_free(&file);
+ serd_node_free(&manifest);
+ serd_env_free(env);
+
+ if (rfd) {
+ lilv_flock(rfd, false);
+ fclose(rfd);
+ }
+
+ return 0;
+}
+
+static bool
+link_exists(const char* path, void* data)
+{
+ const char* target = (const char*)data;
+ if (!lilv_path_exists(path, NULL)) {
+ return false;
+ }
+ char* real_path = lilv_realpath(path);
+ bool matches = !strcmp(real_path, target);
+ free(real_path);
+ return !matches;
+}
+
+static void
+write_property_array(const LilvState* state,
+ const PropertyArray* array,
+ Sratom* sratom,
+ uint32_t flags,
+ const SerdNode* subject,
+ LV2_URID_Unmap* unmap,
+ const char* dir)
+{
+ for (uint32_t i = 0; i < array->n; ++i) {
+ Property* prop = &array->props[i];
+ const char* key = unmap->unmap(unmap->handle, prop->key);
+
+ const SerdNode p = serd_node_from_string(SERD_URI, USTR(key));
+ if (prop->type == state->atom_Path && !dir) {
+ const char* path = (const char*)prop->value;
+ const char* abs_path = lilv_state_rel2abs(state, path);
+ LILV_WARNF("Writing absolute path %s\n", abs_path);
+ sratom_write(sratom, unmap, flags,
+ subject, &p, prop->type,
+ strlen(abs_path) + 1, abs_path);
+ } else if (prop->flags & LV2_STATE_IS_POD ||
+ prop->type == state->atom_Path) {
+ sratom_write(sratom, unmap, flags,
+ subject, &p, prop->type, prop->size, prop->value);
+ } else {
+ LILV_WARNF("Lost non-POD property <%s> on save\n", key);
+ }
+ }
+}
+
+static int
+lilv_state_write(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ SerdWriter* writer,
+ const char* uri,
+ const char* dir)
+{
+ SerdNode lv2_appliesTo = serd_node_from_string(
+ SERD_CURIE, USTR("lv2:appliesTo"));
+
+ const SerdNode* plugin_uri = sord_node_to_serd_node(
+ state->plugin_uri->node);
+
+ SerdNode subject = serd_node_from_string(SERD_URI, USTR(uri ? uri : ""));
+
+ // <subject> a pset:Preset
+ SerdNode p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type"));
+ SerdNode o = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset));
+ serd_writer_write_statement(writer, 0, NULL,
+ &subject, &p, &o, NULL, NULL);
+
+ // <subject> lv2:appliesTo <http://example.org/plugin>
+ serd_writer_write_statement(writer, 0, NULL,
+ &subject,
+ &lv2_appliesTo,
+ plugin_uri, NULL, NULL);
+
+ // <subject> rdfs:label label
+ if (state->label) {
+ p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "label"));
+ o = serd_node_from_string(SERD_LITERAL, USTR(state->label));
+ serd_writer_write_statement(writer, 0,
+ NULL, &subject, &p, &o, NULL, NULL);
+ }
+
+ SerdEnv* env = serd_writer_get_env(writer);
+ const SerdNode* base = serd_env_get_base_uri(env, NULL);
+
+ Sratom* sratom = sratom_new(map);
+ sratom_set_sink(sratom, (const char*)base->buf,
+ (SerdStatementSink)serd_writer_write_statement,
+ (SerdEndSink)serd_writer_end_anon,
+ writer);
+
+ // Write metadata
+ sratom_set_pretty_numbers(sratom, false); // Use precise types
+ write_property_array(state, &state->metadata, sratom, 0,
+ &subject, unmap, dir);
+
+ // Write port values
+ sratom_set_pretty_numbers(sratom, true); // Use pretty numbers
+ for (uint32_t i = 0; i < state->n_values; ++i) {
+ PortValue* const value = &state->values[i];
+
+ const SerdNode port = serd_node_from_string(
+ SERD_BLANK, USTR(value->symbol));
+
+ // <> lv2:port _:symbol
+ p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__port));
+ serd_writer_write_statement(writer, SERD_ANON_O_BEGIN,
+ NULL, &subject, &p, &port, NULL, NULL);
+
+ // _:symbol lv2:symbol "symbol"
+ p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__symbol));
+ o = serd_node_from_string(SERD_LITERAL, USTR(value->symbol));
+ serd_writer_write_statement(writer, SERD_ANON_CONT,
+ NULL, &port, &p, &o, NULL, NULL);
+
+ // _:symbol pset:value value
+ p = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__value));
+ sratom_write(sratom, unmap, SERD_ANON_CONT, &port, &p,
+ value->type, value->size, value->value);
+
+ serd_writer_end_anon(writer, &port);
+ }
+
+ // Write properties
+ const SerdNode body = serd_node_from_string(SERD_BLANK, USTR("body"));
+ if (state->props.n > 0) {
+ p = serd_node_from_string(SERD_URI, USTR(LV2_STATE__state));
+ serd_writer_write_statement(writer, SERD_ANON_O_BEGIN, NULL,
+ &subject, &p, &body, NULL, NULL);
+ }
+ sratom_set_pretty_numbers(sratom, false); // Use precise types
+ write_property_array(state, &state->props, sratom, SERD_ANON_CONT,
+ &body, unmap, dir);
+
+ if (state->props.n > 0) {
+ serd_writer_end_anon(writer, &body);
+ }
+
+ sratom_free(sratom);
+ return 0;
+}
+
+static void
+lilv_state_make_links(const LilvState* state, const char* dir)
+{
+ // Create symlinks to files
+ for (ZixTreeIter* i = zix_tree_begin(state->abs2rel);
+ i != zix_tree_end(state->abs2rel);
+ i = zix_tree_iter_next(i)) {
+ const PathMap* pm = (const PathMap*)zix_tree_get(i);
+
+ char* path = lilv_path_join(dir, pm->rel);
+ if (lilv_path_is_child(pm->abs, state->copy_dir)
+ && strcmp(state->copy_dir, dir)) {
+ // Link directly to snapshot in the copy directory
+ char* target = lilv_path_relative_to(pm->abs, dir);
+ lilv_symlink(target, path);
+ free(target);
+ } else if (!lilv_path_is_child(pm->abs, dir)) {
+ const char* link_dir = state->link_dir ? state->link_dir : dir;
+ char* pat = lilv_path_join(link_dir, pm->rel);
+ if (!strcmp(dir, link_dir)) {
+ // Link directory is save directory, make link at exact path
+ remove(pat);
+ lilv_symlink(pm->abs, pat);
+ } else {
+ // Make a link in the link directory to external file
+ char* lpath = lilv_find_free_path(pat, link_exists, pm->abs);
+ if (!lilv_path_exists(lpath, NULL)) {
+ lilv_symlink(pm->abs, lpath);
+ }
+
+ // Make a link in the save directory to the external link
+ char* target = lilv_path_relative_to(lpath, dir);
+ lilv_symlink(target, path);
+ free(target);
+ free(lpath);
+ }
+ free(pat);
+ }
+ free(path);
+ }
+}
+
+LILV_API int
+lilv_state_save(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* dir,
+ const char* filename)
+{
+ if (!filename || !dir || lilv_mkdir_p(dir)) {
+ return 1;
+ }
+
+ char* abs_dir = absolute_dir(dir);
+ char* const path = lilv_path_join(abs_dir, filename);
+ FILE* fd = fopen(path, "w");
+ if (!fd) {
+ LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno));
+ free(abs_dir);
+ free(path);
+ return 4;
+ }
+
+ // Create symlinks to files if necessary
+ lilv_state_make_links(state, abs_dir);
+
+ // Write state to Turtle file
+ SerdNode file = serd_node_new_file_uri(USTR(path), NULL, NULL, true);
+ SerdNode node = uri ? serd_node_from_string(SERD_URI, USTR(uri)) : file;
+ SerdEnv* env = NULL;
+ SerdWriter* ttl = ttl_file_writer(fd, &file, &env);
+ int ret = lilv_state_write(
+ world, map, unmap, state, ttl, (const char*)node.buf, dir);
+
+ // Set saved dir and uri (FIXME: const violation)
+ SerdNode dir_uri = serd_node_new_file_uri(USTR(abs_dir), NULL, NULL, true);
+ free(state->dir);
+ lilv_node_free(state->uri);
+ ((LilvState*)state)->dir = (char*)dir_uri.buf;
+ ((LilvState*)state)->uri = lilv_new_uri(world, (const char*)node.buf);
+
+ serd_node_free(&file);
+ serd_writer_free(ttl);
+ serd_env_free(env);
+ fclose(fd);
+
+ // Add entry to manifest
+ char* const manifest = lilv_path_join(abs_dir, "manifest.ttl");
+ add_state_to_manifest(world, state->plugin_uri, manifest, uri, path);
+
+ free(manifest);
+ free(abs_dir);
+ free(path);
+ return ret;
+}
+
+LILV_API char*
+lilv_state_to_string(LilvWorld* world,
+ LV2_URID_Map* map,
+ LV2_URID_Unmap* unmap,
+ const LilvState* state,
+ const char* uri,
+ const char* base_uri)
+{
+ if (!uri) {
+ LILV_ERROR("Attempt to serialise state with no URI\n");
+ return NULL;
+ }
+
+ SerdChunk chunk = { NULL, 0 };
+ SerdEnv* env = NULL;
+ SerdNode base = serd_node_from_string(SERD_URI, USTR(base_uri));
+ SerdWriter* writer = ttl_writer(serd_chunk_sink, &chunk, &base, &env);
+
+ lilv_state_write(world, map, unmap, state, writer, uri, NULL);
+
+ serd_writer_free(writer);
+ serd_env_free(env);
+ return (char*)serd_chunk_sink_finish(&chunk);
+}
+
+LILV_API int
+lilv_state_delete(LilvWorld* world,
+ const LilvState* state)
+{
+ if (!state->dir || !state->uri) {
+ LILV_ERROR("Attempt to delete unsaved state\n");
+ return -1;
+ }
+
+ LilvNode* bundle = lilv_new_uri(world, state->dir);
+ LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle);
+ char* manifest_path = lilv_node_get_path(manifest, NULL);
+ SordModel* model = sord_new(world->world, SORD_SPO, false);
+
+ {
+ // Read manifest into temporary local model
+ SerdEnv* env = serd_env_new(sord_node_to_serd_node(manifest->node));
+ SerdReader* ttl = sord_new_reader(model, env, SERD_TURTLE, NULL);
+ serd_reader_read_file(ttl, USTR(manifest_path));
+ serd_reader_free(ttl);
+ serd_env_free(env);
+ }
+
+ SordNode* file = sord_get(
+ model, state->uri->node, world->uris.rdfs_seeAlso, NULL, NULL);
+ if (file) {
+ // Remove state file
+ char* path = lilv_file_uri_parse(
+ (const char*)sord_node_get_string(file), NULL);
+ if (unlink(path)) {
+ LILV_ERRORF("Failed to remove %s (%s)\n", path, strerror(errno));
+ }
+ lilv_free(path);
+ }
+
+ // Remove any existing manifest entries for this state
+ remove_manifest_entry(
+ world->world, model, lilv_node_as_string(state->uri));
+ remove_manifest_entry(
+ world->world, world->model, lilv_node_as_string(state->uri));
+
+ // Drop bundle from model
+ lilv_world_unload_bundle(world, bundle);
+
+ if (sord_num_quads(model) == 0) {
+ // Manifest is empty, attempt to remove bundle entirely
+ if (unlink(manifest_path)) {
+ LILV_ERRORF("Failed to remove %s (%s)\n",
+ manifest_path, strerror(errno));
+ }
+ char* dir_path = lilv_file_uri_parse(state->dir, NULL);
+ if (rmdir(dir_path)) {
+ LILV_ERRORF("Failed to remove %s (%s)\n",
+ dir_path, strerror(errno));
+ }
+ lilv_free(dir_path);
+ } else {
+ // Still something in the manifest, reload bundle
+ lilv_world_load_bundle(world, bundle);
+ }
+
+ sord_free(model);
+ lilv_free(manifest_path);
+ lilv_node_free(manifest);
+ lilv_node_free(bundle);
+
+ return 0;
+}
+
+static void
+free_property_array(LilvState* state, PropertyArray* array)
+{
+ for (uint32_t i = 0; i < array->n; ++i) {
+ Property* prop = &array->props[i];
+ if ((prop->flags & LV2_STATE_IS_POD) ||
+ prop->type == state->atom_Path) {
+ free(prop->value);
+ }
+ }
+ free(array->props);
+}
+
+LILV_API void
+lilv_state_free(LilvState* state)
+{
+ if (state) {
+ free_property_array(state, &state->props);
+ free_property_array(state, &state->metadata);
+ for (uint32_t i = 0; i < state->n_values; ++i) {
+ free(state->values[i].value);
+ free(state->values[i].symbol);
+ }
+ lilv_node_free(state->plugin_uri);
+ lilv_node_free(state->uri);
+ zix_tree_free(state->abs2rel);
+ zix_tree_free(state->rel2abs);
+ free(state->values);
+ free(state->label);
+ free(state->dir);
+ free(state->file_dir);
+ free(state->copy_dir);
+ free(state->link_dir);
+ free(state);
+ }
+}
+
+LILV_API bool
+lilv_state_equals(const LilvState* a, const LilvState* b)
+{
+ if (!lilv_node_equals(a->plugin_uri, b->plugin_uri)
+ || (a->label && !b->label)
+ || (b->label && !a->label)
+ || (a->label && b->label && strcmp(a->label, b->label))
+ || a->props.n != b->props.n
+ || a->n_values != b->n_values) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < a->n_values; ++i) {
+ PortValue* const av = &a->values[i];
+ PortValue* const bv = &b->values[i];
+ if (av->size != bv->size || av->type != bv->type
+ || strcmp(av->symbol, bv->symbol)
+ || memcmp(av->value, bv->value, av->size)) {
+ return false;
+ }
+ }
+
+ for (uint32_t i = 0; i < a->props.n; ++i) {
+ Property* const ap = &a->props.props[i];
+ Property* const bp = &b->props.props[i];
+ if (ap->key != bp->key
+ || ap->type != bp->type
+ || ap->flags != bp->flags) {
+ return false;
+ } else if (ap->type == a->atom_Path) {
+ if (!lilv_file_equals(lilv_state_rel2abs(a, (char*)ap->value),
+ lilv_state_rel2abs(b, (char*)bp->value))) {
+ return false;
+ }
+ } else if (ap->size != bp->size
+ || memcmp(ap->value, bp->value, ap->size)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+LILV_API unsigned
+lilv_state_get_num_properties(const LilvState* state)
+{
+ return state->props.n;
+}
+
+LILV_API const LilvNode*
+lilv_state_get_plugin_uri(const LilvState* state)
+{
+ return state->plugin_uri;
+}
+
+LILV_API const LilvNode*
+lilv_state_get_uri(const LilvState* state)
+{
+ return state->uri;
+}
+
+LILV_API const char*
+lilv_state_get_label(const LilvState* state)
+{
+ return state->label;
+}
+
+LILV_API void
+lilv_state_set_label(LilvState* state, const char* label)
+{
+ const size_t len = strlen(label);
+ state->label = (char*)realloc(state->label, len + 1);
+ memcpy(state->label, label, len + 1);
+}
+
+LILV_API int
+lilv_state_set_metadata(LilvState* state,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ uint32_t flags)
+{
+ append_property(state, &state->metadata, key, value, size, type, flags);
+ return LV2_STATE_SUCCESS;
+}
diff --git a/src/ui.c b/src/ui.c
new file mode 100644
index 0000000..e3a9b2b
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,111 @@
+/*
+ Copyright 2007-2014 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);
+ lilv_node_free(ui->bundle_uri);
+ lilv_node_free(ui->binary_uri);
+ lilv_nodes_free(ui->classes);
+ free(ui);
+}
+
+LILV_API const LilvNode*
+lilv_ui_get_uri(const LilvUI* ui)
+{
+ 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* class_uri)
+{
+ return lilv_nodes_contains(ui->classes, class_uri);
+}
+
+LILV_API const LilvNode*
+lilv_ui_get_bundle_uri(const LilvUI* ui)
+{
+ return ui->bundle_uri;
+}
+
+LILV_API const LilvNode*
+lilv_ui_get_binary_uri(const LilvUI* ui)
+{
+ return ui->binary_uri;
+}
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..4973181
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,640 @@
+/*
+ Copyright 2007-2016 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 200809L /* for fileno */
+#define _BSD_SOURCE 1 /* for realpath, symlink */
+#define _DEFAULT_SOURCE 1 /* for realpath, symlink */
+
+#ifdef __APPLE__
+# define _DARWIN_C_SOURCE 1 /* for flock */
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+
+#ifdef _WIN32
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */
+#endif
+# include <windows.h>
+# include <direct.h>
+# include <io.h>
+# define F_OK 0
+# define mkdir(path, flags) _mkdir(path)
+# if defined(_MSC_VER) && _MSC_VER <= 1400
+/** Implement 'CreateSymbolicLink()' for MSVC 8 or earlier */
+extern "C" BOOLEAN WINAPI
+CreateSymbolicLink(LPCTSTR linkpath, LPCTSTR targetpath, DWORD flags)
+{
+ typedef BOOLEAN (WINAPI* PFUNC)(LPCTSTR, LPCTSTR, DWORD);
+
+ PFUNC pfn = (PFUNC)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+ "CreateSymbolicLinkA");
+ return pfn ? pfn(linkpath, targetpath, flags) : 0;
+}
+# endif
+#else
+# include <dirent.h>
+# include <limits.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
+
+void
+lilv_free(void* ptr)
+{
+ free(ptr);
+}
+
+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);
+ char* new_result = (char*)realloc(result, len + this_len + 1);
+ if (!new_result) {
+ free(result);
+ return NULL;
+ }
+
+ result = new_result;
+ 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* copy = (char*)malloc(len + 1);
+ memcpy(copy, str, len + 1);
+ return copy;
+}
+
+const char*
+lilv_uri_to_path(const char* uri)
+{
+ return (const char*)serd_uri_to_path((const uint8_t*)uri);
+}
+
+char*
+lilv_file_uri_parse(const char* uri, char** hostname)
+{
+ return (char*)serd_file_uri_parse((const uint8_t*)uri, (uint8_t**)hostname);
+}
+
+/** 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 `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)
+{
+#ifdef HAVE_LSTAT
+ struct stat st;
+ return !lstat(path, &st);
+#else
+ return !access(path, F_OK);
+#endif
+}
+
+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) {
+ return errno;
+ }
+
+ FILE* out = fopen(dst, "w");
+ if (!out) {
+ fclose(in);
+ return errno;
+ }
+
+ char* page = (char*)malloc(PAGE_SIZE);
+ size_t n_read = 0;
+ int st = 0;
+ while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) {
+ if (fwrite(page, 1, n_read, out) != n_read) {
+ st = errno;
+ break;
+ }
+ }
+
+ if (!st && (ferror(in) || ferror(out))) {
+ st = EBADF;
+ }
+
+ free(page);
+ fclose(in);
+ fclose(out);
+
+ return st;
+}
+
+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;
+}
+
+typedef struct {
+ char* pattern;
+ 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) {
+ struct stat st;
+ if (!stat(entry_path, &st)) {
+ if (st.st_mtime >= latest->time) {
+ free(latest->latest);
+ latest->latest = entry_path;
+ }
+ } else {
+ LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
+ }
+ }
+ if (entry_path != latest->latest) {
+ free(entry_path);
+ }
+}
+
+/** Return the latest copy of the file at `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, NULL };
+
+ struct stat st;
+ if (!stat(path, &st)) {
+ latest.time = st.st_mtime;
+ } else {
+ LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno));
+ }
+
+ lilv_dir_for_each(copy_dir, &latest, update_latest);
+
+ free(latest.pattern);
+ free(copy_dir);
+ return latest.latest;
+}
+
+char*
+lilv_realpath(const char* path)
+{
+ if (!path) {
+ return NULL;
+ }
+
+#if defined(_WIN32)
+ char* out = (char*)malloc(MAX_PATH);
+ GetFullPathName(path, MAX_PATH, out, NULL);
+ return out;
+#elif _POSIX_VERSION >= 200809L
+ char* real_path = realpath(path, NULL);
+ return real_path ? real_path : lilv_strdup(path);
+#else
+ // OSX <= 10.5, if anyone cares. I sure don't.
+ char* out = (char*)malloc(PATH_MAX);
+ char* real_path = realpath(path, out);
+ if (!real_path) {
+ free(out);
+ return lilv_strdup(path);
+ } else {
+ return real_path;
+ }
+#endif
+}
+
+int
+lilv_symlink(const char* oldpath, const char* newpath)
+{
+ int ret = 0;
+ if (strcmp(oldpath, newpath)) {
+#ifdef _WIN32
+ ret = !CreateSymbolicLink(newpath, oldpath, 0);
+ if (ret) {
+ ret = !CreateHardLink(newpath, oldpath, 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) {
+ for (struct dirent* entry; (entry = readdir(dir));) {
+ 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) {
+ free(path);
+ return errno;
+ }
+ 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 (!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")) ||
+ !(b_file = fopen(b_real, "rb"))) {
+ match = false; // Missing file matches nothing
+ } else {
+ // TODO: Improve performance by reading chunks
+ match = true;
+ 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;
+}
diff --git a/src/world.c b/src/world.c
new file mode 100644
index 0000000..1858569
--- /dev/null
+++ b/src/world.c
@@ -0,0 +1,1212 @@
+/*
+ Copyright 2007-2016 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/lv2plug.in/ns/ext/presets/presets.h"
+
+#include "lilv_internal.h"
+
+static int
+lilv_world_drop_graph(LilvWorld* world, const SordNode* graph);
+
+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->zombies = lilv_plugins_new();
+ world->loaded_files = zix_tree_new(
+ false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free);
+
+ world->libs = zix_tree_new(false, lilv_lib_compare, NULL, NULL);
+
+#define NS_DCTERMS "http://purl.org/dc/terms/"
+#define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#"
+#define NS_OWL "http://www.w3.org/2002/07/owl#"
+
+#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_latency = NEW_URI(LV2_CORE__latency);
+ world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum);
+ world->uris.lv2_microVersion = NEW_URI(LV2_CORE__microVersion);
+ world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum);
+ world->uris.lv2_minorVersion = NEW_URI(LV2_CORE__minorVersion);
+ 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.lv2_prototype = NEW_URI(LV2_CORE__prototype);
+ world->uris.owl_Ontology = NEW_URI(NS_OWL "Ontology");
+ 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;
+
+ LILV_FOREACH(plugins, i, world->zombies) {
+ const LilvPlugin* p = lilv_plugins_get(world->zombies, i);
+ lilv_plugin_free((LilvPlugin*)p);
+ }
+ zix_tree_free((ZixTree*)world->zombies);
+ world->zombies = NULL;
+
+ zix_tree_free((ZixTree*)world->loaded_files);
+ world->loaded_files = NULL;
+
+ zix_tree_free(world->libs);
+ world->libs = NULL;
+
+ 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* uri,
+ const LilvNode* value)
+{
+ if (!strcmp(uri, LILV_OPTION_DYN_MANIFEST)) {
+ if (lilv_node_is_bool(value)) {
+ world->opt.dyn_manifest = lilv_node_as_bool(value);
+ return;
+ }
+ } else if (!strcmp(uri, 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", uri);
+}
+
+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;
+ } else if (!predicate) {
+ LILV_ERROR("Missing required predicate\n");
+ return NULL;
+ } else if (!lilv_node_is_uri(predicate)) {
+ LILV_ERRORF("Predicate `%s' is not a URI\n",
+ sord_node_get_string(predicate->node));
+ return NULL;
+ } else if (!subject && !object) {
+ LILV_ERROR("Both subject and object are NULL\n");
+ return NULL;
+ }
+
+ return lilv_world_find_nodes_internal(world,
+ subject ? subject->node : NULL,
+ predicate->node,
+ object ? object->node : NULL);
+}
+
+LILV_API LilvNode*
+lilv_world_get(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object)
+{
+ SordNode* snode = sord_get(world->model,
+ subject ? subject->node : NULL,
+ predicate ? predicate->node : NULL,
+ object ? object->node : NULL,
+ NULL);
+ LilvNode* lnode = lilv_node_new_from_node(world, snode);
+ sord_node_free(world->world, snode);
+ return lnode;
+}
+
+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);
+}
+
+bool
+lilv_world_ask_internal(LilvWorld* world,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object)
+{
+ return sord_ask(world->model, subject, predicate, object, NULL);
+}
+
+LILV_API bool
+lilv_world_ask(LilvWorld* world,
+ const LilvNode* subject,
+ const LilvNode* predicate,
+ const LilvNode* object)
+{
+ return sord_ask(world->model,
+ subject ? subject->node : NULL,
+ predicate ? predicate->node : NULL,
+ object ? object->node : NULL,
+ NULL);
+}
+
+SordModel*
+lilv_world_filter_model(LilvWorld* world,
+ SordModel* model,
+ const SordNode* subject,
+ const SordNode* predicate,
+ const SordNode* object,
+ const SordNode* graph)
+{
+ SordModel* results = sord_new(world->world, SORD_SPO, false);
+ SordIter* i = sord_search(model, subject, predicate, object, graph);
+ for (; !sord_iter_end(i); sord_iter_next(i)) {
+ SordQuad quad;
+ sord_iter_get(i, quad);
+ sord_add(results, quad);
+ }
+ sord_iter_free(i);
+ return results;
+}
+
+LilvNodes*
+lilv_world_find_nodes_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;
+ serd_uri_parse(base_uri_str, &base_uri);
+ return serd_node_new_uri_from_string(uri_str, &base_uri, NULL);
+}
+
+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));
+}
+
+/**
+ Comparator for libraries (world->libs).
+
+ Libraries do have a LilvHeader, but we must also compare the bundle to
+ handle the case where the same library is loaded with different bundles, and
+ consequently different contents (mainly plugins).
+ */
+int
+lilv_lib_compare(const void* a, const void* b, void* user_data)
+{
+ const LilvLib* const lib_a = (const LilvLib*)a;
+ const LilvLib* const lib_b = (const LilvLib*)b;
+ int cmp = strcmp(lilv_node_as_uri(lib_a->uri),
+ lilv_node_as_uri(lib_b->uri));
+ return cmp ? cmp : strcmp(lib_a->bundle_path, lib_b->bundle_path);
+}
+
+/** Get an element of a collection of any object with an LilvHeader by URI. */
+static ZixTreeIter*
+lilv_collection_find_by_uri(const ZixTree* seq, const LilvNode* uri)
+{
+ ZixTreeIter* i = NULL;
+ if (lilv_node_is_uri(uri)) {
+ struct LilvHeader key = { NULL, (LilvNode*)uri };
+ zix_tree_find(seq, &key, &i);
+ }
+ return i;
+}
+
+/** Get an element of a collection of any object with an LilvHeader by URI. */
+struct LilvHeader*
+lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri)
+{
+ ZixTreeIter* const i = lilv_collection_find_by_uri(seq, uri);
+
+ return i ? (struct LilvHeader*)zix_tree_get(i) : 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 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,
+ const LilvNode* manifest_uri,
+ void* dynmanifest,
+ const SordNode* bundle)
+{
+ LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node);
+ ZixTreeIter* z = NULL;
+ LilvPlugin* plugin = (LilvPlugin*)lilv_plugins_get_by_uri(
+ world->plugins, plugin_uri);
+
+ if (plugin) {
+ // Existing plugin, if this is different bundle, ignore it
+ // (use the first plugin found in LV2_PATH)
+ const LilvNode* last_bundle = lilv_plugin_get_bundle_uri(plugin);
+ const char* plugin_uri_str = lilv_node_as_uri(plugin_uri);
+ if (sord_node_equals(bundle, last_bundle->node)) {
+ LILV_WARNF("Reloading plugin <%s>\n", plugin_uri_str);
+ plugin->loaded = false;
+ lilv_node_free(plugin_uri);
+ } else {
+ LILV_WARNF("Duplicate plugin <%s>\n", plugin_uri_str);
+ LILV_WARNF("... found in %s\n", lilv_node_as_string(last_bundle));
+ LILV_WARNF("... and %s (ignored)\n", sord_node_get_string(bundle));
+ lilv_node_free(plugin_uri);
+ return;
+ }
+ } else if ((z = lilv_collection_find_by_uri((const ZixTree*)world->zombies,
+ plugin_uri))) {
+ // Plugin bundle has been re-loaded, move from zombies to plugins
+ plugin = (LilvPlugin*)zix_tree_get(z);
+ zix_tree_remove((ZixTree*)world->zombies, z);
+ zix_tree_insert((ZixTree*)world->plugins, plugin, NULL);
+ lilv_node_free(plugin_uri);
+ lilv_plugin_clear(plugin, lilv_node_new_from_node(world, bundle));
+ } else {
+ // Add new plugin to the world
+ plugin = lilv_plugin_new(
+ world, plugin_uri, lilv_node_new_from_node(world, bundle));
+
+ // Add manifest as plugin data file (as if it were rdfs:seeAlso)
+ zix_tree_insert((ZixTree*)plugin->data_uris,
+ lilv_node_duplicate(manifest_uri),
+ NULL);
+
+ // Add plugin to world plugin sequence
+ zix_tree_insert((ZixTree*)world->plugins, plugin, 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);
+}
+
+SerdStatus
+lilv_world_load_graph(LilvWorld* world, SordNode* graph, const LilvNode* uri)
+{
+ const SerdNode* base = sord_node_to_serd_node(uri->node);
+ SerdEnv* env = serd_env_new(base);
+ SerdReader* reader = sord_new_reader(
+ world->model, env, SERD_TURTLE, graph);
+
+ const SerdStatus st = lilv_world_load_file(world, reader, uri);
+
+ serd_env_free(env);
+ serd_reader_free(reader);
+ return st;
+}
+
+static void
+lilv_world_load_dyn_manifest(LilvWorld* world,
+ SordNode* bundle_node,
+ const LilvNode* manifest)
+{
+#ifdef LILV_DYN_MANIFEST
+ if (!world->opt.dyn_manifest) {
+ return;
+ }
+
+ LV2_Dyn_Manifest_Handle handle = NULL;
+
+ // ?dman a dynman:DynManifest bundle_node
+ SordModel* model = lilv_world_filter_model(world,
+ world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.dman_DynManifest,
+ bundle_node);
+ SordIter* iter = sord_begin(model);
+ for (; !sord_iter_end(iter); sord_iter_next(iter)) {
+ const SordNode* dmanifest = sord_iter_get_node(iter, 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);
+ char* lib_path = lilv_file_uri_parse((const char*)lib_uri, 0);
+ if (!lib_path) {
+ LILV_ERROR("No dynamic manifest library path\n");
+ sord_iter_free(binaries);
+ continue;
+ }
+
+ // Open library
+ dlerror();
+ void* lib = dlopen(lib_path, RTLD_LAZY);
+ if (!lib) {
+ LILV_ERRORF("Failed to open dynmanifest library `%s' (%s)\n",
+ lib_path, dlerror());
+ sord_iter_free(binaries);
+ lilv_free(lib_path);
+ 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);
+ lilv_free(lib_path);
+ 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);
+ lilv_free(lib_path);
+ 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;
+
+ sord_iter_free(binaries);
+
+ // Generate data file
+ FILE* fd = tmpfile();
+ get_subjects_func(handle, fd);
+ rewind(fd);
+
+ // Parse generated data file into temporary model
+ // FIXME
+ 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
+ SordModel* plugins = lilv_world_filter_model(world,
+ world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin,
+ dmanifest);
+ SordIter* p = sord_begin(plugins);
+ FOREACH_MATCH(p) {
+ const SordNode* plug = sord_iter_get_node(p, SORD_SUBJECT);
+ lilv_world_add_plugin(world, plug, manifest, desc, bundle_node);
+ }
+ if (desc->refs == 0) {
+ free(desc);
+ }
+ sord_iter_free(p);
+ sord_free(plugins);
+ lilv_free(lib_path);
+ }
+ sord_iter_free(iter);
+ sord_free(model);
+#endif // LILV_DYN_MANIFEST
+}
+
+LilvNode*
+lilv_world_get_manifest_uri(LilvWorld* world, const LilvNode* bundle_uri)
+{
+ SerdNode manifest_uri = lilv_new_uri_relative_to_base(
+ (const uint8_t*)"manifest.ttl",
+ sord_node_get_string(bundle_uri->node));
+ LilvNode* manifest = lilv_new_uri(world, (const char*)manifest_uri.buf);
+ serd_node_free(&manifest_uri);
+ return manifest;
+}
+
+static SordModel*
+load_plugin_model(LilvWorld* world,
+ const LilvNode* bundle_uri,
+ const LilvNode* plugin_uri)
+{
+ // Create model and reader for loading into it
+ SordNode* bundle_node = bundle_uri->node;
+ SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false);
+ SerdEnv* env = serd_env_new(sord_node_to_serd_node(bundle_node));
+ SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
+
+ // Load manifest
+ LilvNode* manifest_uri = lilv_world_get_manifest_uri(world, bundle_uri);
+ serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world));
+ serd_reader_read_file(
+ reader, (const uint8_t*)lilv_node_as_string(manifest_uri));
+
+ // Load any seeAlso files
+ SordModel* files = lilv_world_filter_model(
+ world, model, plugin_uri->node, world->uris.rdfs_seeAlso, NULL, NULL);
+
+ SordIter* f = sord_begin(files);
+ FOREACH_MATCH(f) {
+ const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
+ const uint8_t* file_str = sord_node_get_string(file);
+ if (sord_node_get_type(file) == SORD_URI) {
+ serd_reader_add_blank_prefix(
+ reader, lilv_world_blank_node_prefix(world));
+ serd_reader_read_file(reader, file_str);
+ }
+ }
+
+ sord_iter_free(f);
+ sord_free(files);
+ serd_reader_free(reader);
+ serd_env_free(env);
+ lilv_node_free(manifest_uri);
+
+ return model;
+}
+
+static LilvVersion
+get_version(LilvWorld* world, SordModel* model, const LilvNode* subject)
+{
+ const SordNode* minor_node = sord_get(
+ model, subject->node, world->uris.lv2_minorVersion, NULL, NULL);
+ const SordNode* micro_node = sord_get(
+ model, subject->node, world->uris.lv2_microVersion, NULL, NULL);
+
+
+ LilvVersion version = { 0, 0 };
+ if (minor_node && micro_node) {
+ version.minor = atoi((const char*)sord_node_get_string(minor_node));
+ version.micro = atoi((const char*)sord_node_get_string(micro_node));
+ }
+
+ return version;
+}
+
+LILV_API void
+lilv_world_load_bundle(LilvWorld* world, const 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;
+ LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle_uri);
+
+ // Read manifest into model with graph = bundle_node
+ SerdStatus st = lilv_world_load_graph(world, bundle_node, manifest);
+ if (st > SERD_FAILURE) {
+ LILV_ERRORF("Error reading %s\n", lilv_node_as_string(manifest));
+ lilv_node_free(manifest);
+ return;
+ }
+
+ // ?plugin a lv2:Plugin
+ SordIter* plug_results = sord_search(world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin,
+ bundle_node);
+
+ // Find any loaded plugins that will be replaced with a newer version
+ LilvNodes* unload_uris = lilv_nodes_new();
+ FOREACH_MATCH(plug_results) {
+ const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT);
+
+ LilvNode* plugin_uri = lilv_node_new_from_node(world, plug);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, plugin_uri);
+ const LilvNode* last_bundle = plugin ? lilv_plugin_get_bundle_uri(plugin) : NULL;
+ if (!plugin || sord_node_equals(bundle_node, last_bundle->node)) {
+ // No previously loaded version, or it's from the same bundle
+ lilv_node_free(plugin_uri);
+ continue;
+ }
+
+ // Compare versions
+ SordModel* this_model = load_plugin_model(world, bundle_uri, plugin_uri);
+ LilvVersion this_version = get_version(world, this_model, plugin_uri);
+ SordModel* last_model = load_plugin_model(world, last_bundle, plugin_uri);
+ LilvVersion last_version = get_version(world, last_model, plugin_uri);
+ sord_free(this_model);
+ sord_free(last_model);
+ const int cmp = lilv_version_cmp(&this_version, &last_version);
+ if (cmp > 0) {
+ zix_tree_insert((ZixTree*)unload_uris,
+ lilv_node_duplicate(plugin_uri),
+ NULL);
+ LILV_WARNF("Replacing version %d.%d of <%s> from <%s>\n",
+ last_version.minor, last_version.micro,
+ sord_node_get_string(plug),
+ sord_node_get_string(last_bundle->node));
+ LILV_NOTEF("New version %d.%d found in <%s>\n",
+ this_version.minor, this_version.micro,
+ sord_node_get_string(bundle_node));
+ } else if (cmp < 0) {
+ LILV_WARNF("Ignoring bundle <%s>\n",
+ sord_node_get_string(bundle_node));
+ LILV_NOTEF("Newer version of <%s> loaded from <%s>\n",
+ sord_node_get_string(plug),
+ sord_node_get_string(last_bundle->node));
+ lilv_node_free(plugin_uri);
+ sord_iter_free(plug_results);
+ lilv_world_drop_graph(world, bundle_node);
+ lilv_node_free(manifest);
+ lilv_nodes_free(unload_uris);
+ return;
+ }
+ lilv_node_free(plugin_uri);
+ }
+
+ sord_iter_free(plug_results);
+
+ // Unload any old conflicting plugins
+ LilvNodes* unload_bundles = lilv_nodes_new();
+ LILV_FOREACH(nodes, i, unload_uris) {
+ const LilvNode* uri = lilv_nodes_get(unload_uris, i);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, uri);
+ const LilvNode* bundle = lilv_plugin_get_bundle_uri(plugin);
+
+ // Unload plugin and record bundle for later unloading
+ lilv_world_unload_resource(world, uri);
+ zix_tree_insert((ZixTree*)unload_bundles,
+ lilv_node_duplicate(bundle),
+ NULL);
+
+ }
+ lilv_nodes_free(unload_uris);
+
+ // Now unload the associated bundles
+ // This must be done last since several plugins could be in the same bundle
+ LILV_FOREACH(nodes, i, unload_bundles) {
+ lilv_world_unload_bundle(world, lilv_nodes_get(unload_bundles, i));
+ }
+ lilv_nodes_free(unload_bundles);
+
+ // Re-search for plugin results now that old plugins are gone
+ plug_results = sord_search(world->model,
+ NULL,
+ world->uris.rdf_a,
+ world->uris.lv2_Plugin,
+ bundle_node);
+
+ FOREACH_MATCH(plug_results) {
+ const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT);
+ lilv_world_add_plugin(world, plug, manifest, NULL, bundle_node);
+ }
+ sord_iter_free(plug_results);
+
+ lilv_world_load_dyn_manifest(world, bundle_node, manifest);
+
+ // ?spec a lv2:Specification
+ // ?spec a owl:Ontology
+ const SordNode* spec_preds[] = { world->uris.lv2_Specification,
+ world->uris.owl_Ontology,
+ NULL };
+ for (const SordNode** p = spec_preds; *p; ++p) {
+ SordIter* i = sord_search(
+ world->model, NULL, world->uris.rdf_a, *p, bundle_node);
+ FOREACH_MATCH(i) {
+ const SordNode* spec = sord_iter_get_node(i, SORD_SUBJECT);
+ lilv_world_add_spec(world, spec, bundle_node);
+ }
+ sord_iter_free(i);
+ }
+
+ lilv_node_free(manifest);
+}
+
+static int
+lilv_world_drop_graph(LilvWorld* world, const SordNode* graph)
+{
+ SordIter* i = sord_search(world->model, NULL, NULL, NULL, graph);
+ while (!sord_iter_end(i)) {
+ const SerdStatus st = sord_erase(world->model, i);
+ if (st) {
+ LILV_ERRORF("Error removing statement from <%s> (%s)\n",
+ sord_node_get_string(graph), serd_strerror(st));
+ return st;
+ }
+ }
+ sord_iter_free(i);
+
+ return 0;
+}
+
+/** Remove loaded_files entry so file will be reloaded if requested. */
+static int
+lilv_world_unload_file(LilvWorld* world, const LilvNode* file)
+{
+ ZixTreeIter* iter;
+ if (!zix_tree_find((ZixTree*)world->loaded_files, file, &iter)) {
+ zix_tree_remove((ZixTree*)world->loaded_files, iter);
+ return 0;
+ }
+ return 1;
+}
+
+LILV_API int
+lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri)
+{
+ if (!bundle_uri) {
+ return 0;
+ }
+
+ // Find all loaded files that are inside the bundle
+ LilvNodes* files = lilv_nodes_new();
+ LILV_FOREACH(nodes, i, world->loaded_files) {
+ const LilvNode* file = lilv_nodes_get(world->loaded_files, i);
+ if (!strncmp(lilv_node_as_string(file),
+ lilv_node_as_string(bundle_uri),
+ strlen(lilv_node_as_string(bundle_uri)))) {
+ zix_tree_insert((ZixTree*)files,
+ lilv_node_duplicate(file),
+ NULL);
+ }
+ }
+
+ // Unload all loaded files in the bundle
+ LILV_FOREACH(nodes, i, files) {
+ const LilvNode* file = lilv_nodes_get(world->plugins, i);
+ lilv_world_unload_file(world, file);
+ }
+
+ lilv_nodes_free(files);
+
+ /* Remove any plugins in the bundle from the plugin list. Since the
+ application may still have a pointer to the LilvPlugin, it can not be
+ destroyed here. Instead, we move it to the zombie plugin list, so it
+ will not be in the list returned by lilv_world_get_all_plugins() but can
+ still be used.
+ */
+ ZixTreeIter* i = zix_tree_begin((ZixTree*)world->plugins);
+ while (i != zix_tree_end((ZixTree*)world->plugins)) {
+ LilvPlugin* p = (LilvPlugin*)zix_tree_get(i);
+ ZixTreeIter* next = zix_tree_iter_next(i);
+
+ if (lilv_node_equals(lilv_plugin_get_bundle_uri(p), bundle_uri)) {
+ zix_tree_remove((ZixTree*)world->plugins, i);
+ zix_tree_insert((ZixTree*)world->zombies, p, NULL);
+ }
+
+ i = next;
+ }
+
+ // Drop everything in bundle graph
+ return lilv_world_drop_graph(world, bundle_uri->node);
+}
+
+static void
+load_dir_entry(const char* dir, const char* name, void* data)
+{
+ LilvWorld* world = (LilvWorld*)data;
+ if (!strcmp(name, ".") || !strcmp(name, "..")) {
+ return;
+ }
+
+ char* path = lilv_strjoin(dir, "/", name, "/", NULL);
+ SerdNode suri = serd_node_new_file_uri((const uint8_t*)path, 0, 0, true);
+ LilvNode* node = lilv_new_uri(world, (const char*)suri.buf);
+
+ lilv_world_load_bundle(world, node);
+ lilv_node_free(node);
+ serd_node_free(&suri);
+ free(path);
+}
+
+/** Load all bundles in the directory at `dir_path`. */
+static void
+lilv_world_load_directory(LilvWorld* world, const char* dir_path)
+{
+ char* path = lilv_expand(dir_path);
+ if (path) {
+ lilv_dir_for_each(path, world, load_dir_entry);
+ free(path);
+ }
+}
+
+static const char*
+first_path_sep(const char* path)
+{
+ for (const char* p = path; *p != '\0'; ++p) {
+ if (*p == LILV_PATH_SEP[0]) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+/** Load all bundles found in `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";
+ }
+ }
+}
+
+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);
+ lilv_world_load_graph(world, NULL, file);
+ }
+ }
+}
+
+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);
+
+ SordNode* parent = sord_get(
+ world->model, class_node, world->uris.rdfs_subClassOf, NULL, NULL);
+ if (!parent || sord_node_get_type(parent) != SORD_URI) {
+ continue;
+ }
+
+ SordNode* label = sord_get(
+ world->model, class_node, world->uris.rdfs_label, NULL, NULL);
+ if (!label) {
+ sord_node_free(world->world, parent);
+ continue;
+ }
+
+ LilvPluginClass* pclass = lilv_plugin_class_new(
+ world, parent, class_node,
+ (const char*)sord_node_get_string(label));
+ if (pclass) {
+ zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL);
+ }
+
+ sord_node_free(world->world, label);
+ sord_node_free(world->world, parent);
+ }
+ 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
+ if (sord_ask(world->model,
+ NULL,
+ world->uris.dc_replaces,
+ lilv_plugin_get_uri(plugin)->node,
+ NULL)) {
+ // TODO: Check if replacement is a known plugin? (expensive)
+ ((LilvPlugin*)plugin)->replaced = true;
+ }
+ }
+
+ // Query out things to cache
+ lilv_world_load_specifications(world);
+ lilv_world_load_plugin_classes(world);
+}
+
+SerdStatus
+lilv_world_load_file(LilvWorld* world, SerdReader* reader, const LilvNode* uri)
+{
+ ZixTreeIter* iter;
+ if (!zix_tree_find((ZixTree*)world->loaded_files, uri, &iter)) {
+ return SERD_FAILURE; // File has already been loaded
+ }
+
+ size_t uri_len;
+ const uint8_t* const uri_str = sord_node_get_string_counted(
+ uri->node, &uri_len);
+ if (strncmp((const char*)uri_str, "file:", 5)) {
+ return SERD_FAILURE; // Not a local file
+ } else if (strcmp((const char*)uri_str + uri_len - 4, ".ttl")) {
+ return SERD_FAILURE; // Not a Turtle file
+ }
+
+ serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world));
+ const SerdStatus st = serd_reader_read_file(reader, uri_str);
+ if (st) {
+ LILV_ERRORF("Error loading file `%s'\n", lilv_node_as_string(uri));
+ return st;
+ }
+
+ zix_tree_insert((ZixTree*)world->loaded_files,
+ lilv_node_duplicate(uri),
+ NULL);
+ return SERD_SUCCESS;
+}
+
+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;
+ }
+
+ SordModel* files = lilv_world_filter_model(world,
+ world->model,
+ resource->node,
+ world->uris.rdfs_seeAlso,
+ NULL, NULL);
+
+ SordIter* f = sord_begin(files);
+ int n_read = 0;
+ FOREACH_MATCH(f) {
+ const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
+ const uint8_t* file_str = sord_node_get_string(file);
+ LilvNode* file_node = lilv_node_new_from_node(world, file);
+ if (sord_node_get_type(file) != SORD_URI) {
+ LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", file_str);
+ } else if (!lilv_world_load_graph(world, (SordNode*)file, file_node)) {
+ ++n_read;
+ }
+ lilv_node_free(file_node);
+ }
+ sord_iter_free(f);
+
+ sord_free(files);
+ return n_read;
+}
+
+LILV_API int
+lilv_world_unload_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;
+ }
+
+ SordModel* files = lilv_world_filter_model(world,
+ world->model,
+ resource->node,
+ world->uris.rdfs_seeAlso,
+ NULL, NULL);
+
+ SordIter* f = sord_begin(files);
+ int n_dropped = 0;
+ FOREACH_MATCH(f) {
+ const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
+ LilvNode* file_node = lilv_node_new_from_node(world, file);
+ if (sord_node_get_type(file) != SORD_URI) {
+ LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n",
+ sord_node_get_string(file));
+ } else if (!lilv_world_drop_graph(world, file_node->node)) {
+ lilv_world_unload_file(world, file_node);
+ ++n_dropped;
+ }
+ lilv_node_free(file_node);
+ }
+ sord_iter_free(f);
+
+ sord_free(files);
+ return n_dropped;
+}
+
+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;
+}
+
+LILV_API LilvNode*
+lilv_world_get_symbol(LilvWorld* world, const LilvNode* subject)
+{
+ // Check for explicitly given symbol
+ SordNode* snode = sord_get(
+ world->model, subject->node, world->uris.lv2_symbol, NULL, NULL);
+
+ if (snode) {
+ LilvNode* ret = lilv_node_new_from_node(world, snode);
+ sord_node_free(world->world, snode);
+ return ret;
+ }
+
+ if (!lilv_node_is_uri(subject)) {
+ return NULL;
+ }
+
+ // Find rightmost segment of URI
+ SerdURI uri;
+ serd_uri_parse((const uint8_t*)lilv_node_as_uri(subject), &uri);
+ const char* str = "_";
+ if (uri.fragment.buf) {
+ str = (const char*)uri.fragment.buf + 1;
+ } else if (uri.query.buf) {
+ str = (const char*)uri.query.buf;
+ } else if (uri.path.buf) {
+ const char* last_slash = strrchr((const char*)uri.path.buf, '/');
+ str = last_slash ? (last_slash + 1) : (const char*)uri.path.buf;
+ }
+
+ // Replace invalid characters
+ const size_t len = strlen(str);
+ char* const sym = (char*)calloc(1, len + 1);
+ for (size_t i = 0; i < len; ++i) {
+ const char c = str[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c == '_') || (i > 0 && c >= '0' && c <= '9'))) {
+ sym[i] = '_';
+ } else {
+ sym[i] = str[i];
+ }
+ }
+
+ LilvNode* ret = lilv_new_string(world, sym);
+ free(sym);
+ return ret;
+}
diff --git a/src/zix/common.h b/src/zix/common.h
new file mode 100644
index 0000000..32019e9
--- /dev/null
+++ b/src/zix/common.h
@@ -0,0 +1,88 @@
+/*
+ Copyright 2016 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 */
diff --git a/src/zix/tree.c b/src/zix/tree.c
new file mode 100644
index 0000000..1646709
--- /dev/null
+++ b/src/zix/tree.c
@@ -0,0 +1,716 @@
+/*
+ Copyright 2011-2014 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 `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 `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 `p->left` then right about `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 `p->right` then right about `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(const 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(const ZixTreeIter* i)
+{
+ return !i;
+}
+
+ZIX_API bool
+zix_tree_iter_is_rend(const 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;
+}
diff --git a/src/zix/tree.h b/src/zix/tree.h
new file mode 100644
index 0000000..e531a9b
--- /dev/null
+++ b/src/zix/tree.h
@@ -0,0 +1,148 @@
+/*
+ Copyright 2011-2014 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 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 `t`.
+*/
+ZIX_API void
+zix_tree_free(ZixTree* t);
+
+/**
+ Return the number of elements in `t`.
+*/
+ZIX_API size_t
+zix_tree_size(const ZixTree* t);
+
+/**
+ Insert the element `e` into `t` and point `ti` at the new element.
+*/
+ZIX_API ZixStatus
+zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti);
+
+/**
+ Remove the item pointed at by `ti` from `t`.
+*/
+ZIX_API ZixStatus
+zix_tree_remove(ZixTree* t, ZixTreeIter* ti);
+
+/**
+ Set `ti` to an element equal to `e` in `t`.
+ If no such item exists, `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(const ZixTreeIter* ti);
+
+/**
+ Return an iterator to the first (smallest) element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_begin(ZixTree* t);
+
+/**
+ Return an iterator the the element one past the last element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_end(ZixTree* t);
+
+/**
+ Return true iff `i` is an iterator to the end of its tree.
+*/
+ZIX_API bool
+zix_tree_iter_is_end(const ZixTreeIter* i);
+
+/**
+ Return an iterator to the last (largest) element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_rbegin(ZixTree* t);
+
+/**
+ Return an iterator the the element one before the first element in `t`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_rend(ZixTree* t);
+
+/**
+ Return true iff `i` is an iterator to the reverse end of its tree.
+*/
+ZIX_API bool
+zix_tree_iter_is_rend(const ZixTreeIter* i);
+
+/**
+ Return an iterator that points to the element one past `i`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_iter_next(ZixTreeIter* i);
+
+/**
+ Return an iterator that points to the element one before `i`.
+*/
+ZIX_API ZixTreeIter*
+zix_tree_iter_prev(ZixTreeIter* i);
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ZIX_TREE_H */
diff --git a/test/bad_syntax.lv2/bad_syntax.c b/test/bad_syntax.lv2/bad_syntax.c
new file mode 100644
index 0000000..52620bd
--- /dev/null
+++ b/test/bad_syntax.lv2/bad_syntax.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Bad syntax in plugin data file
+ Copyright 2011-2016 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/bad-syntax"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/bad_syntax.lv2/bad_syntax.ttl.in b/test/bad_syntax.lv2/bad_syntax.ttl.in
new file mode 100644
index 0000000..a96d1fc
--- /dev/null
+++ b/test/bad_syntax.lv2/bad_syntax.ttl.in
@@ -0,0 +1,22 @@
+# Lilv Test Plugin - Bad syntax in plugin data file
+# Copyright 2011-2016 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/bad-syntax>
+ a plugin with a clearly broken data file \ No newline at end of file
diff --git a/test/bad_syntax.lv2/manifest.ttl.in b/test/bad_syntax.lv2/manifest.ttl.in
new file mode 100644
index 0000000..6350d8d
--- /dev/null
+++ b/test/bad_syntax.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/bad-syntax>
+ a lv2:Plugin ;
+ lv2:binary <bad_syntax@SHLIB_EXT@> ;
+ rdfs:seeAlso <bad_syntax.ttl> .
diff --git a/test/bad_syntax.lv2/test_bad_syntax.c b/test/bad_syntax.lv2/test_bad_syntax.c
new file mode 100644
index 0000000..d20b8b7
--- /dev/null
+++ b/test/bad_syntax.lv2/test_bad_syntax.c
@@ -0,0 +1,45 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/bad-syntax"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+
+ TEST_ASSERT(!lilv_plugin_get_name(plugin));
+ TEST_ASSERT(!lilv_plugin_instantiate(plugin, 48000, NULL));
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/failed_instantiation.lv2/failed_instantiation.c b/test/failed_instantiation.lv2/failed_instantiation.c
new file mode 100644
index 0000000..aa90532
--- /dev/null
+++ b/test/failed_instantiation.lv2/failed_instantiation.c
@@ -0,0 +1,70 @@
+/*
+ Lilv Test Plugin - Failed instantiation
+ Copyright 2011-2016 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/failed-instantiation"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void* data)
+{}
+
+static LV2_Handle
+instantiate(const LV2_Descriptor* descriptor,
+ double rate,
+ const char* path,
+ const LV2_Feature* const* features)
+{
+ return NULL;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/failed_instantiation.lv2/failed_instantiation.ttl.in b/test/failed_instantiation.lv2/failed_instantiation.ttl.in
new file mode 100644
index 0000000..8c7f678
--- /dev/null
+++ b/test/failed_instantiation.lv2/failed_instantiation.ttl.in
@@ -0,0 +1,40 @@
+# Lilv Test Plugin - Failed instantiation
+# Copyright 2011-2016 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/failed-instantiation>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ doap:name "New version" ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:minorVersion 2 ;
+ lv2:microVersion 1 ;
+ 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"
+ ] .
diff --git a/test/failed_instantiation.lv2/manifest.ttl.in b/test/failed_instantiation.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d55a573
--- /dev/null
+++ b/test/failed_instantiation.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/failed-instantiation>
+ a lv2:Plugin ;
+ lv2:binary <failed_instantiation@SHLIB_EXT@> ;
+ rdfs:seeAlso <failed_instantiation.ttl> .
diff --git a/test/failed_instantiation.lv2/test_failed_instantiation.c b/test/failed_instantiation.lv2/test_failed_instantiation.c
new file mode 100644
index 0000000..6efa0ae
--- /dev/null
+++ b/test/failed_instantiation.lv2/test_failed_instantiation.c
@@ -0,0 +1,45 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/failed-instantiation"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ TEST_ASSERT(!lilv_plugin_instantiate(plugin, 48000, NULL));
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/failed_lib_descriptor.lv2/failed_lib_descriptor.c b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.c
new file mode 100644
index 0000000..6a1fd90
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.c
@@ -0,0 +1,30 @@
+/*
+ Lilv Test Plugin - Failed lib descriptor
+ Copyright 2011-2015 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/failed-lib-descriptor"
+
+LV2_SYMBOL_EXPORT
+const LV2_Lib_Descriptor*
+lv2_lib_descriptor(const char* bundle_path,
+ const LV2_Feature*const* features)
+{
+ return NULL;
+}
diff --git a/test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in
new file mode 100644
index 0000000..f41545f
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in
@@ -0,0 +1,38 @@
+# Lilv Test Plugin - Failed lib descriptor
+# Copyright 2011-2015 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/failed-lib-descriptor>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/failed_lib_descriptor.lv2/manifest.ttl.in b/test/failed_lib_descriptor.lv2/manifest.ttl.in
new file mode 100644
index 0000000..4724f0f
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/failed-lib-descriptor>
+ a lv2:Plugin ;
+ lv2:binary <failed_lib_descriptor@SHLIB_EXT@> ;
+ rdfs:seeAlso <failed_lib_descriptor.ttl> .
diff --git a/test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c b/test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c
new file mode 100644
index 0000000..b82eb0a
--- /dev/null
+++ b/test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c
@@ -0,0 +1,46 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/failed-lib-descriptor"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(!instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/lib_descriptor.lv2/lib_descriptor.c b/test/lib_descriptor.lv2/lib_descriptor.c
new file mode 100644
index 0000000..48dc775
--- /dev/null
+++ b/test/lib_descriptor.lv2/lib_descriptor.c
@@ -0,0 +1,112 @@
+/*
+ Lilv Test Plugin - Missing descriptor
+ Copyright 2011-2015 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/lib-descriptor"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+static const LV2_Descriptor*
+get_plugin(LV2_Lib_Handle handle, uint32_t index)
+{
+ switch (index) {
+ case 0:
+ return &descriptor;
+ default:
+ return NULL;
+ }
+}
+
+static const LV2_Lib_Descriptor lib = {
+ NULL,
+ sizeof(LV2_Lib_Descriptor),
+ NULL,
+ get_plugin };
+
+LV2_SYMBOL_EXPORT
+const LV2_Lib_Descriptor*
+lv2_lib_descriptor(const char* bundle_path,
+ const LV2_Feature*const* features)
+{
+ return &lib;
+}
diff --git a/test/lib_descriptor.lv2/lib_descriptor.ttl.in b/test/lib_descriptor.lv2/lib_descriptor.ttl.in
new file mode 100644
index 0000000..19c8c4a
--- /dev/null
+++ b/test/lib_descriptor.lv2/lib_descriptor.ttl.in
@@ -0,0 +1,41 @@
+# Lilv Test Plugin - Missing descriptor
+# Copyright 2011-2015 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#> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<http://example.org/lib-descriptor>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ <http://example.org/blob> "aGVsbG8sIHdvcmxk"^^xsd:base64Binary ;
+ <http://example.org/junk> "opaque"^^<http://example.org/binary> ;
+ 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"
+ ] .
diff --git a/test/lib_descriptor.lv2/manifest.ttl.in b/test/lib_descriptor.lv2/manifest.ttl.in
new file mode 100644
index 0000000..5f4eced
--- /dev/null
+++ b/test/lib_descriptor.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/lib-descriptor>
+ a lv2:Plugin ;
+ lv2:binary <lib_descriptor@SHLIB_EXT@> ;
+ rdfs:seeAlso <lib_descriptor.ttl> .
diff --git a/test/lib_descriptor.lv2/test_lib_descriptor.c b/test/lib_descriptor.lv2/test_lib_descriptor.c
new file mode 100644
index 0000000..ff14763
--- /dev/null
+++ b/test/lib_descriptor.lv2/test_lib_descriptor.c
@@ -0,0 +1,59 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/lib-descriptor"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(instance);
+ lilv_instance_free(instance);
+
+ LilvNode* eg_blob = lilv_new_uri(world, "http://example.org/blob");
+ LilvNode* blob = lilv_world_get(world, plugin_uri, eg_blob, NULL);
+ TEST_ASSERT(lilv_node_is_literal(blob));
+ lilv_node_free(blob);
+ lilv_node_free(eg_blob);
+
+ LilvNode* eg_junk = lilv_new_uri(world, "http://example.org/junk");
+ LilvNode* junk = lilv_world_get(world, plugin_uri, eg_junk, NULL);
+ TEST_ASSERT(lilv_node_is_literal(junk));
+ lilv_node_free(junk);
+ lilv_node_free(eg_junk);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/lilv_cxx_test.cpp b/test/lilv_cxx_test.cpp
new file mode 100644
index 0000000..25f30bf
--- /dev/null
+++ b/test/lilv_cxx_test.cpp
@@ -0,0 +1,23 @@
+/*
+ Copyright 2017 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/lilvmm.hpp"
+
+int
+main()
+{
+ return 0;
+}
diff --git a/test/lilv_test.c b/test/lilv_test.c
new file mode 100644
index 0000000..6b81cb1
--- /dev/null
+++ b/test/lilv_test.c
@@ -0,0 +1,2296 @@
+/*
+ Copyright 2007-2016 David Robillard <http://drobilla.net>
+ Copyright 2008 Krzysztof Foltman
+
+ 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 200809L /* for setenv */
+#define _XOPEN_SOURCE 600 /* for mkstemp */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <direct.h>
+# include <io.h>
+# define mkdir(path, flags) _mkdir(path)
+# define setenv(n, v, r) SetEnvironmentVariable((n), (v))
+# define unsetenv(n) SetEnvironmentVariable((n), NULL)
+# define mkstemp(pat) _mktemp(pat)
+#else
+# include <dirent.h>
+# include <unistd.h>
+#endif
+
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+
+#define TEST_PATH_MAX 1024
+
+#if defined(__APPLE__)
+# define SHLIB_EXT ".dylib"
+#elif defined(_WIN32)
+# define SHLIB_EXT ".dll"
+#else
+# define SHLIB_EXT ".so"
+#endif
+
+static char bundle_dir_name[TEST_PATH_MAX + sizeof("/.lv2/lilv-test.lv2")];
+static char bundle_dir_uri[sizeof(bundle_dir_name) + sizeof("file:///")];
+static char manifest_name[sizeof(bundle_dir_name) + sizeof("/manifest.ttl")];
+static char content_name[sizeof(bundle_dir_name) + sizeof("plugin.ttl")];
+
+static LilvWorld* world;
+
+int test_count = 0;
+int error_count = 0;
+
+static void
+delete_bundle(void)
+{
+ unlink(content_name);
+ unlink(manifest_name);
+ remove(bundle_dir_name);
+}
+
+static void
+init_tests(void)
+{
+ snprintf(bundle_dir_name, strlen(bundle_dir_name), "%s/.lv2/lilv-test.lv2",
+ getenv("HOME"));
+ lilv_mkdir_p(bundle_dir_name);
+
+ snprintf(bundle_dir_uri, sizeof(bundle_dir_uri), "file://%s/",
+ bundle_dir_name);
+ snprintf(manifest_name, sizeof(manifest_name), "%s/manifest.ttl",
+ bundle_dir_name);
+ snprintf(content_name, sizeof(content_name), "%s/plugin.ttl",
+ bundle_dir_name);
+
+ delete_bundle();
+}
+
+static void
+fatal_error(const char* err, const char* arg)
+{
+ /* TODO: possibly change to vfprintf later */
+ fprintf(stderr, err, arg);
+ /* IMHO, the bundle should be left in place after an error, for possible investigation */
+ /* delete_bundle(); */
+ exit(1);
+}
+
+static void
+write_file(const char* name, const char* content)
+{
+ FILE* f = fopen(name, "w");
+ size_t len = strlen(content);
+ if (fwrite(content, 1, len, f) != len) {
+ fatal_error("Cannot write file %s\n", name);
+ }
+ fclose(f);
+}
+
+static int
+init_world(void)
+{
+ world = lilv_world_new();
+ return world != NULL;
+}
+
+static int
+load_all_bundles(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+ lilv_world_load_all(world);
+ return 1;
+}
+
+static void
+create_bundle(const char* manifest, const char* content)
+{
+ if (mkdir(bundle_dir_name, 0700) && errno != EEXIST) {
+ fatal_error("Cannot create directory %s\n", bundle_dir_name);
+ }
+ write_file(manifest_name, manifest);
+ write_file(content_name, content);
+}
+
+static int
+start_bundle(const char* manifest, const char* content)
+{
+ create_bundle(manifest, content);
+ return load_all_bundles();
+}
+
+static void
+unload_bundle(void)
+{
+ if (world) {
+ lilv_world_free(world);
+ }
+ world = NULL;
+}
+
+static void
+cleanup(void)
+{
+ delete_bundle();
+}
+
+/*****************************************************************************/
+
+#define TEST_CASE(name) { #name, test_##name }
+#define TEST_ASSERT(check) do {\
+ test_count++;\
+ if (!(check)) {\
+ error_count++;\
+ fprintf(stderr, "lilv_test.c:%d: error: test `%s' failed\n", __LINE__, #check);\
+ abort();\
+ }\
+} while (0)
+
+typedef int (*TestFunc)(void);
+
+struct TestCase {
+ const char* title;
+ TestFunc func;
+};
+
+#define PREFIX_ATOM "@prefix atom: <http://lv2plug.in/ns/ext/atom#> . \n"
+#define PREFIX_LINE "@prefix : <http://example.org/> .\n"
+#define PREFIX_LV2 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n"
+#define PREFIX_LV2EV "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> . \n"
+#define PREFIX_LV2UI "@prefix lv2ui: <http://lv2plug.in/ns/extensions/ui#> .\n"
+#define PREFIX_RDF "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"
+#define PREFIX_RDFS "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"
+#define PREFIX_FOAF "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"
+#define PREFIX_DOAP "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"
+#define PREFIX_PSET "@prefix pset: <http://lv2plug.in/ns/ext/presets#> .\n"
+
+#define MANIFEST_PREFIXES PREFIX_LINE PREFIX_LV2 PREFIX_RDFS
+#define BUNDLE_PREFIXES PREFIX_ATOM PREFIX_LINE PREFIX_LV2 PREFIX_RDF PREFIX_RDFS PREFIX_FOAF PREFIX_DOAP PREFIX_PSET
+#define PLUGIN_NAME(name) "doap:name \"" name "\""
+#define LICENSE_GPL "doap:license <http://usefulinc.com/doap/licenses/gpl>"
+
+static const char* uris_plugin = "http://example.org/plug";
+static LilvNode* plugin_uri_value;
+static LilvNode* plugin2_uri_value;
+
+/*****************************************************************************/
+
+static void
+init_uris(void)
+{
+ plugin_uri_value = lilv_new_uri(world, uris_plugin);
+ plugin2_uri_value = lilv_new_uri(world, "http://example.org/foobar");
+ TEST_ASSERT(plugin_uri_value);
+ TEST_ASSERT(plugin2_uri_value);
+}
+
+static void
+cleanup_uris(void)
+{
+ lilv_node_free(plugin2_uri_value);
+ lilv_node_free(plugin_uri_value);
+ plugin2_uri_value = NULL;
+ plugin_uri_value = NULL;
+}
+
+/*****************************************************************************/
+
+static int
+test_value(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"Foo\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ LilvNode* uval = lilv_new_uri(world, "http://example.org");
+ LilvNode* sval = lilv_new_string(world, "Foo");
+ LilvNode* ival = lilv_new_int(world, 42);
+ LilvNode* fval = lilv_new_float(world, 1.6180);
+
+ TEST_ASSERT(lilv_node_is_uri(uval));
+ TEST_ASSERT(lilv_node_is_string(sval));
+ TEST_ASSERT(lilv_node_is_int(ival));
+ TEST_ASSERT(lilv_node_is_float(fval));
+
+ TEST_ASSERT(!lilv_node_is_literal(NULL));
+ TEST_ASSERT(!lilv_node_is_literal(uval));
+ TEST_ASSERT(lilv_node_is_literal(sval));
+ TEST_ASSERT(lilv_node_is_literal(ival));
+ TEST_ASSERT(lilv_node_is_literal(fval));
+ TEST_ASSERT(!lilv_node_get_path(fval, NULL));
+
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(uval), "http://example.org"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(sval), "Foo"));
+ TEST_ASSERT(lilv_node_as_int(ival) == 42);
+ TEST_ASSERT(fabs(lilv_node_as_float(fval) - 1.6180) < FLT_EPSILON);
+ TEST_ASSERT(isnan(lilv_node_as_float(sval)));
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+ TEST_ASSERT(!strcmp(lilv_uri_to_path("file:///foo"), "/foo"));
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# pragma GCC diagnostic pop
+#endif
+
+ LilvNode* loc_abs = lilv_new_file_uri(world, NULL, "/foo/bar");
+ LilvNode* loc_rel = lilv_new_file_uri(world, NULL, "foo");
+ LilvNode* host_abs = lilv_new_file_uri(world, "host", "/foo/bar");
+ LilvNode* host_rel = lilv_new_file_uri(world, "host", "foo");
+
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(loc_abs), "file:///foo/bar"));
+ TEST_ASSERT(!strncmp(lilv_node_as_uri(loc_rel), "file:///", 8));
+ TEST_ASSERT(!strcmp(lilv_node_as_uri(host_abs), "file://host/foo/bar"));
+ TEST_ASSERT(!strncmp(lilv_node_as_uri(host_rel), "file://host/", 12));
+
+ lilv_node_free(host_rel);
+ lilv_node_free(host_abs);
+ lilv_node_free(loc_rel);
+ lilv_node_free(loc_abs);
+
+ char* tok = lilv_node_get_turtle_token(uval);
+ TEST_ASSERT(!strcmp(tok, "<http://example.org>"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(sval);
+ TEST_ASSERT(!strcmp(tok, "Foo"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(ival);
+ TEST_ASSERT(!strcmp(tok, "42"));
+ lilv_free(tok);
+ tok = lilv_node_get_turtle_token(fval);
+ TEST_ASSERT(!strncmp(tok, "1.6180", 6));
+ lilv_free(tok);
+
+ LilvNode* uval_e = lilv_new_uri(world, "http://example.org");
+ LilvNode* sval_e = lilv_new_string(world, "Foo");
+ LilvNode* ival_e = lilv_new_int(world, 42);
+ LilvNode* fval_e = lilv_new_float(world, 1.6180);
+ LilvNode* uval_ne = lilv_new_uri(world, "http://no-example.org");
+ LilvNode* sval_ne = lilv_new_string(world, "Bar");
+ LilvNode* ival_ne = lilv_new_int(world, 24);
+ LilvNode* fval_ne = lilv_new_float(world, 3.14159);
+
+ TEST_ASSERT(lilv_node_equals(uval, uval_e));
+ TEST_ASSERT(lilv_node_equals(sval, sval_e));
+ TEST_ASSERT(lilv_node_equals(ival, ival_e));
+ TEST_ASSERT(lilv_node_equals(fval, fval_e));
+
+ TEST_ASSERT(!lilv_node_equals(uval, uval_ne));
+ TEST_ASSERT(!lilv_node_equals(sval, sval_ne));
+ TEST_ASSERT(!lilv_node_equals(ival, ival_ne));
+ TEST_ASSERT(!lilv_node_equals(fval, fval_ne));
+
+ TEST_ASSERT(!lilv_node_equals(uval, sval));
+ TEST_ASSERT(!lilv_node_equals(sval, ival));
+ TEST_ASSERT(!lilv_node_equals(ival, fval));
+
+ LilvNode* uval_dup = lilv_node_duplicate(uval);
+ TEST_ASSERT(lilv_node_equals(uval, uval_dup));
+
+ LilvNode* ifval = lilv_new_float(world, 42.0);
+ TEST_ASSERT(!lilv_node_equals(ival, ifval));
+ lilv_node_free(ifval);
+
+ LilvNode* nil = NULL;
+ TEST_ASSERT(!lilv_node_equals(uval, nil));
+ TEST_ASSERT(!lilv_node_equals(nil, uval));
+ TEST_ASSERT(lilv_node_equals(nil, nil));
+
+ LilvNode* nil2 = lilv_node_duplicate(nil);
+ TEST_ASSERT(lilv_node_equals(nil, nil2));
+
+ lilv_node_free(uval);
+ lilv_node_free(sval);
+ lilv_node_free(ival);
+ lilv_node_free(fval);
+ lilv_node_free(uval_e);
+ lilv_node_free(sval_e);
+ lilv_node_free(ival_e);
+ lilv_node_free(fval_e);
+ lilv_node_free(uval_ne);
+ lilv_node_free(sval_ne);
+ lilv_node_free(ival_ne);
+ lilv_node_free(fval_ne);
+ lilv_node_free(uval_dup);
+ lilv_node_free(nil2);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_util(void)
+{
+ TEST_ASSERT(!lilv_realpath(NULL));
+
+ char a_path[16];
+ char b_path[16];
+ strncpy(a_path, "copy_a_XXXXXX", sizeof(a_path));
+ strncpy(b_path, "copy_b_XXXXXX", sizeof(b_path));
+ mkstemp(a_path);
+ mkstemp(b_path);
+
+ FILE* fa = fopen(a_path, "w");
+ FILE* fb = fopen(b_path, "w");
+ fprintf(fa, "AA\n");
+ fprintf(fb, "AB\n");
+ fclose(fa);
+ fclose(fb);
+
+ TEST_ASSERT(lilv_copy_file("does/not/exist", "copy"));
+ TEST_ASSERT(lilv_copy_file(a_path, "not/a/dir/copy"));
+ TEST_ASSERT(!lilv_copy_file(a_path, "copy_c"));
+ TEST_ASSERT(!lilv_file_equals(a_path, b_path));
+ TEST_ASSERT(lilv_file_equals(a_path, a_path));
+ TEST_ASSERT(lilv_file_equals(a_path, "copy_c"));
+ TEST_ASSERT(!lilv_file_equals("does/not/exist", b_path));
+ TEST_ASSERT(!lilv_file_equals(a_path, "does/not/exist"));
+ TEST_ASSERT(!lilv_file_equals("does/not/exist", "/does/not/either"));
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int discovery_plugin_found = 0;
+
+static void
+discovery_verify_plugin(const LilvPlugin* plugin)
+{
+ const LilvNode* value = lilv_plugin_get_uri(plugin);
+ if (lilv_node_equals(value, plugin_uri_value)) {
+ const LilvNode* lib_uri = NULL;
+ TEST_ASSERT(!lilv_node_equals(value, plugin2_uri_value));
+ discovery_plugin_found = 1;
+ lib_uri = lilv_plugin_get_library_uri(plugin);
+ TEST_ASSERT(lib_uri);
+ TEST_ASSERT(lilv_node_is_uri(lib_uri));
+ TEST_ASSERT(lilv_node_as_uri(lib_uri));
+ TEST_ASSERT(strstr(lilv_node_as_uri(lib_uri), "foo" SHLIB_EXT));
+ TEST_ASSERT(lilv_plugin_verify(plugin));
+ }
+}
+
+static int
+test_discovery(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ;"
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ a lv2:ControlPort ; a lv2:InputPort ;"
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; ] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ TEST_ASSERT(lilv_plugins_size(plugins) > 0);
+
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug != NULL);
+ const LilvPlugin* explug2 = lilv_plugins_get_by_uri(plugins, plugin2_uri_value);
+ TEST_ASSERT(explug2 == NULL);
+
+ if (explug) {
+ LilvNode* name = lilv_plugin_get_name(explug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "Test plugin"));
+ lilv_node_free(name);
+ }
+
+ discovery_plugin_found = 0;
+ LILV_FOREACH(plugins, i, plugins)
+ discovery_verify_plugin(lilv_plugins_get(plugins, i));
+
+ TEST_ASSERT(discovery_plugin_found);
+ plugins = NULL;
+
+ cleanup_uris();
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_lv2_path(void)
+{
+#ifndef _WIN32
+ char* orig_lv2_path = lilv_strdup(getenv("LV2_PATH"));
+
+ setenv("LV2_PATH", "~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2", 1);
+
+ world = lilv_world_new();
+ lilv_world_load_all(world);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const size_t n_plugins = lilv_plugins_size(plugins);
+
+ lilv_world_free(world);
+
+ setenv("LV2_PATH", "$HOME/.lv2:/usr/local/lib/lv2:/usr/lib/lv2", 1);
+ world = lilv_world_new();
+ lilv_world_load_all(world);
+ plugins = lilv_world_get_all_plugins(world);
+ TEST_ASSERT(lilv_plugins_size(plugins) == n_plugins);
+ lilv_world_free(world);
+ world = NULL;
+
+ if (orig_lv2_path) {
+ setenv("LV2_PATH", orig_lv2_path, 1);
+ } else {
+ unsetenv("LV2_PATH");
+ }
+ free(orig_lv2_path);
+#endif
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_verify(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ a lv2:ControlPort ; a lv2:InputPort ;"
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug);
+ TEST_ASSERT(lilv_plugin_verify(explug));
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_no_verify(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin . ")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* explug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(explug);
+ TEST_ASSERT(!lilv_plugin_verify(explug));
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_classes(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"Foo\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPluginClass* plugin = lilv_world_get_plugin_class(world);
+ const LilvPluginClasses* classes = lilv_world_get_plugin_classes(world);
+ LilvPluginClasses* children = lilv_plugin_class_get_children(plugin);
+
+ TEST_ASSERT(lilv_plugin_class_get_parent_uri(plugin) == NULL);
+ TEST_ASSERT(lilv_plugin_classes_size(classes) > lilv_plugin_classes_size(children));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_plugin_class_get_label(plugin)), "Plugin"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_plugin_class_get_uri(plugin)),
+ "http://lv2plug.in/ns/lv2core#Plugin"));
+
+ LILV_FOREACH(plugin_classes, i, children) {
+ TEST_ASSERT(lilv_node_equals(
+ lilv_plugin_class_get_parent_uri(lilv_plugin_classes_get(children, i)),
+ lilv_plugin_class_get_uri(plugin)));
+ }
+
+ LilvNode* some_uri = lilv_new_uri(world, "http://example.org/whatever");
+ TEST_ASSERT(lilv_plugin_classes_get_by_uri(classes, some_uri) == NULL);
+ lilv_node_free(some_uri);
+
+ lilv_plugin_classes_free(children);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_plugin(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:optionalFeature lv2:hardRTCapable ; "
+ "lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
+ "lv2:extensionData <http://example.org/extdata> ;"
+ ":foo 1.6180 ; "
+ ":bar true ; "
+ ":baz false ; "
+ ":blank [ a <http://example.org/blank> ] ; "
+ "doap:maintainer [ foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ const LilvPluginClass* klass = lilv_plugin_get_class(plug);
+ const LilvNode* klass_uri = lilv_plugin_class_get_uri(klass);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(klass_uri),
+ "http://lv2plug.in/ns/lv2core#CompressorPlugin"));
+
+ LilvNode* rdf_type = lilv_new_uri(
+ world, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
+ TEST_ASSERT(lilv_world_ask(world,
+ lilv_plugin_get_uri(plug),
+ rdf_type,
+ klass_uri));
+ lilv_node_free(rdf_type);
+
+ TEST_ASSERT(!lilv_plugin_is_replaced(plug));
+ TEST_ASSERT(!lilv_plugin_get_related(plug, NULL));
+
+ const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(plug_bundle_uri), bundle_dir_uri));
+
+ const LilvNodes* data_uris = lilv_plugin_get_data_uris(plug);
+ TEST_ASSERT(lilv_nodes_size(data_uris) == 2);
+
+ LilvNode* project = lilv_plugin_get_project(plug);
+ TEST_ASSERT(!project);
+
+ char* manifest_uri = (char*)malloc(TEST_PATH_MAX);
+ char* data_uri = (char*)malloc(TEST_PATH_MAX);
+ snprintf(manifest_uri, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "manifest.ttl");
+ snprintf(data_uri, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "plugin.ttl");
+
+ LilvNode* manifest_uri_val = lilv_new_uri(world, manifest_uri);
+ TEST_ASSERT(lilv_nodes_contains(data_uris, manifest_uri_val));
+ lilv_node_free(manifest_uri_val);
+
+ LilvNode* data_uri_val = lilv_new_uri(world, data_uri);
+ TEST_ASSERT(lilv_nodes_contains(data_uris, data_uri_val));
+ lilv_node_free(data_uri_val);
+
+ LilvNode* unknown_uri_val = lilv_new_uri(world, "http://example.org/unknown");
+ TEST_ASSERT(!lilv_nodes_contains(data_uris, unknown_uri_val));
+ lilv_node_free(unknown_uri_val);
+
+ free(manifest_uri);
+ free(data_uri);
+
+ float mins[3];
+ float maxs[3];
+ float defs[3];
+ lilv_plugin_get_port_ranges_float(plug, mins, maxs, defs);
+ TEST_ASSERT(mins[0] == -1.0f);
+ TEST_ASSERT(maxs[0] == 1.0f);
+ TEST_ASSERT(defs[0] == 0.5f);
+
+ LilvNode* audio_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#AudioPort");
+ LilvNode* control_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#ControlPort");
+ LilvNode* in_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#InputPort");
+ LilvNode* out_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#OutputPort");
+
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, NULL) == 3);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, NULL) == 0);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, in_class, NULL) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, out_class, NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, in_class, NULL) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, out_class, NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, in_class, NULL) == 0);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class, out_class, NULL) == 0);
+
+ TEST_ASSERT(lilv_plugin_has_latency(plug));
+ TEST_ASSERT(lilv_plugin_get_latency_port_index(plug) == 2);
+
+ LilvNode* lv2_latency = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#latency");
+ const LilvPort* latency_port = lilv_plugin_get_port_by_designation(
+ plug, out_class, lv2_latency);
+ lilv_node_free(lv2_latency);
+
+ TEST_ASSERT(latency_port);
+ TEST_ASSERT(lilv_port_get_index(plug, latency_port) == 2);
+ TEST_ASSERT(lilv_node_is_blank(lilv_port_get_node(plug, latency_port)));
+
+ LilvNode* rt_feature = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#hardRTCapable");
+ LilvNode* event_feature = lilv_new_uri(world,
+ "http://lv2plug.in/ns/ext/event");
+ LilvNode* pretend_feature = lilv_new_uri(world,
+ "http://example.org/solvesWorldHunger");
+
+ TEST_ASSERT(lilv_plugin_has_feature(plug, rt_feature));
+ TEST_ASSERT(lilv_plugin_has_feature(plug, event_feature));
+ TEST_ASSERT(!lilv_plugin_has_feature(plug, pretend_feature));
+
+ lilv_node_free(rt_feature);
+ lilv_node_free(event_feature);
+ lilv_node_free(pretend_feature);
+
+ LilvNodes* supported = lilv_plugin_get_supported_features(plug);
+ LilvNodes* required = lilv_plugin_get_required_features(plug);
+ LilvNodes* optional = lilv_plugin_get_optional_features(plug);
+ TEST_ASSERT(lilv_nodes_size(supported) == 2);
+ TEST_ASSERT(lilv_nodes_size(required) == 1);
+ TEST_ASSERT(lilv_nodes_size(optional) == 1);
+ lilv_nodes_free(supported);
+ lilv_nodes_free(required);
+ lilv_nodes_free(optional);
+
+ LilvNode* foo_p = lilv_new_uri(world, "http://example.org/foo");
+ LilvNodes* foos = lilv_plugin_get_value(plug, foo_p);
+ TEST_ASSERT(lilv_nodes_size(foos) == 1);
+ TEST_ASSERT(fabs(lilv_node_as_float(lilv_nodes_get_first(foos)) - 1.6180) < FLT_EPSILON);
+ lilv_node_free(foo_p);
+ lilv_nodes_free(foos);
+
+ LilvNode* bar_p = lilv_new_uri(world, "http://example.org/bar");
+ LilvNodes* bars = lilv_plugin_get_value(plug, bar_p);
+ TEST_ASSERT(lilv_nodes_size(bars) == 1);
+ TEST_ASSERT(lilv_node_as_bool(lilv_nodes_get_first(bars)) == true);
+ lilv_node_free(bar_p);
+ lilv_nodes_free(bars);
+
+ LilvNode* baz_p = lilv_new_uri(world, "http://example.org/baz");
+ LilvNodes* bazs = lilv_plugin_get_value(plug, baz_p);
+ TEST_ASSERT(lilv_nodes_size(bazs) == 1);
+ TEST_ASSERT(lilv_node_as_bool(lilv_nodes_get_first(bazs)) == false);
+ lilv_node_free(baz_p);
+ lilv_nodes_free(bazs);
+
+ LilvNode* blank_p = lilv_new_uri(world, "http://example.org/blank");
+ LilvNodes* blanks = lilv_plugin_get_value(plug, blank_p);
+ TEST_ASSERT(lilv_nodes_size(blanks) == 1);
+ LilvNode* blank = lilv_nodes_get_first(blanks);
+ TEST_ASSERT(lilv_node_is_blank(blank));
+ const char* blank_str = lilv_node_as_blank(blank);
+ char* blank_tok = lilv_node_get_turtle_token(blank);
+ TEST_ASSERT(!strncmp(blank_tok, "_:", 2));
+ TEST_ASSERT(!strcmp(blank_tok + 2, blank_str));
+ lilv_free(blank_tok);
+ lilv_node_free(blank_p);
+ lilv_nodes_free(blanks);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_name), "David Robillard"));
+ lilv_node_free(author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_email), "mailto:d@drobilla.net"));
+ lilv_node_free(author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_homepage), "http://drobilla.net"));
+ lilv_node_free(author_homepage);
+
+ LilvNode* thing_uri = lilv_new_uri(world, "http://example.org/thing");
+ LilvNode* name_p = lilv_new_uri(world, "http://usefulinc.com/ns/doap#name");
+ LilvNodes* thing_names = lilv_world_find_nodes(world, thing_uri, name_p, NULL);
+ TEST_ASSERT(lilv_nodes_size(thing_names) == 1);
+ LilvNode* thing_name = lilv_nodes_get_first(thing_names);
+ TEST_ASSERT(thing_name);
+ TEST_ASSERT(lilv_node_is_string(thing_name));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(thing_name), "Something else"));
+ LilvNode* thing_name2 = lilv_world_get(world, thing_uri, name_p, NULL);
+ TEST_ASSERT(lilv_node_equals(thing_name, thing_name2));
+
+ LilvUIs* uis = lilv_plugin_get_uis(plug);
+ TEST_ASSERT(lilv_uis_size(uis) == 0);
+ lilv_uis_free(uis);
+
+ LilvNode* extdata = lilv_new_uri(world, "http://example.org/extdata");
+ LilvNode* noextdata = lilv_new_uri(world, "http://example.org/noextdata");
+ LilvNodes* extdatas = lilv_plugin_get_extension_data(plug);
+ TEST_ASSERT(lilv_plugin_has_extension_data(plug, extdata));
+ TEST_ASSERT(!lilv_plugin_has_extension_data(plug, noextdata));
+ TEST_ASSERT(lilv_nodes_size(extdatas) == 1);
+ TEST_ASSERT(lilv_node_equals(lilv_nodes_get_first(extdatas), extdata));
+ lilv_node_free(noextdata);
+ lilv_node_free(extdata);
+ lilv_nodes_free(extdatas);
+
+ lilv_nodes_free(thing_names);
+ lilv_node_free(thing_uri);
+ lilv_node_free(thing_name2);
+ lilv_node_free(name_p);
+ lilv_node_free(control_class);
+ lilv_node_free(audio_class);
+ lilv_node_free(in_class);
+ lilv_node_free(out_class);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_project(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:maintainer [ "
+ " foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_name), "David Robillard"));
+ lilv_node_free(author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_email), "mailto:d@drobilla.net"));
+ lilv_node_free(author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(author_homepage), "http://drobilla.net"));
+ lilv_node_free(author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_no_author(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_project_no_author(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":thing doap:name \"Something else\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* author_name = lilv_plugin_get_author_name(plug);
+ TEST_ASSERT(!author_name);
+
+ LilvNode* author_email = lilv_plugin_get_author_email(plug);
+ TEST_ASSERT(!author_email);
+
+ LilvNode* author_homepage = lilv_plugin_get_author_homepage(plug);
+ TEST_ASSERT(!author_homepage);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_preset(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin with project") " ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ "<http://example.org/preset> a pset:Preset ;"
+ " lv2:appliesTo :plug ;"
+ " rdfs:label \"some preset\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* pset_Preset = lilv_new_uri(world, LV2_PRESETS__Preset);
+ LilvNodes* related = lilv_plugin_get_related(plug, pset_Preset);
+
+ TEST_ASSERT(lilv_nodes_size(related) == 1);
+
+ lilv_node_free(pset_Preset);
+ lilv_nodes_free(related);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_prototype(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":prot a lv2:PluginBase ; rdfs:seeAlso <plugin.ttl> .\n"
+ ":plug a lv2:Plugin ; lv2:binary <inst" SHLIB_EXT "> ; lv2:prototype :prot .\n",
+ BUNDLE_PREFIXES
+ ":prot a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ LICENSE_GPL " ; "
+ "lv2:project [ "
+ " doap:name \"Fake project\" ;"
+ "] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency ; "
+ " lv2:designation lv2:latency "
+ "] . \n"
+ ":plug doap:name \"Instance\" .\n")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ // Test non-inherited property
+ LilvNode* name = lilv_plugin_get_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "Instance"));
+ lilv_node_free(name);
+
+ // Test inherited property
+ const LilvNode* binary = lilv_plugin_get_library_uri(plug);
+ TEST_ASSERT(strstr(lilv_node_as_string(binary), "inst" SHLIB_EXT));
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_port(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; "
+ " lv2:name \"store\" ; "
+ " lv2:name \"dépanneur\"@fr-ca ; lv2:name \"épicerie\"@fr-fr ; "
+ " lv2:name \"tienda\"@es ; "
+ " rdfs:comment \"comment\"@en , \"commentaires\"@fr ; "
+ " lv2:portProperty lv2:integer ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 ; "
+ " lv2:scalePoint [ rdfs:label \"Sin\"; rdf:value 3 ] ; "
+ " lv2:scalePoint [ rdfs:label \"Cos\"; rdf:value 4 ] "
+ "] , [\n"
+ " a lv2:EventPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"event_in\" ; "
+ " lv2:name \"Event Input\" ; "
+ " lv2ev:supportsEvent <http://example.org/event> ;"
+ " atom:supports <http://example.org/atomEvent> "
+ "] , [\n"
+ " a lv2:AudioPort ; a lv2:InputPort ; "
+ " lv2:index 2 ; lv2:symbol \"audio_in\" ; "
+ " lv2:name \"Audio Input\" ; "
+ "] , [\n"
+ " a lv2:AudioPort ; a lv2:OutputPort ; "
+ " lv2:index 3 ; lv2:symbol \"audio_out\" ; "
+ " lv2:name \"Audio Output\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvNode* psym = lilv_new_string(world, "foo");
+ const LilvPort* p = lilv_plugin_get_port_by_index(plug, 0);
+ const LilvPort* p2 = lilv_plugin_get_port_by_symbol(plug, psym);
+ lilv_node_free(psym);
+ TEST_ASSERT(p != NULL);
+ TEST_ASSERT(p2 != NULL);
+ TEST_ASSERT(p == p2);
+
+ LilvNode* nopsym = lilv_new_string(world, "thisaintnoportfoo");
+ const LilvPort* p3 = lilv_plugin_get_port_by_symbol(plug, nopsym);
+ TEST_ASSERT(p3 == NULL);
+ lilv_node_free(nopsym);
+
+ // Try getting an invalid property
+ LilvNode* num = lilv_new_int(world, 1);
+ LilvNodes* nothing = lilv_port_get_value(plug, p, num);
+ TEST_ASSERT(!nothing);
+ lilv_node_free(num);
+
+ LilvNode* audio_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#AudioPort");
+ LilvNode* control_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#ControlPort");
+ LilvNode* in_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#InputPort");
+ LilvNode* out_class = lilv_new_uri(world,
+ "http://lv2plug.in/ns/lv2core#OutputPort");
+
+ TEST_ASSERT(lilv_nodes_size(lilv_port_get_classes(plug, p)) == 2);
+ TEST_ASSERT(lilv_plugin_get_num_ports(plug) == 4);
+ TEST_ASSERT(lilv_port_is_a(plug, p, control_class));
+ TEST_ASSERT(lilv_port_is_a(plug, p, in_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, p, audio_class));
+
+ LilvNodes* port_properties = lilv_port_get_properties(plug, p);
+ TEST_ASSERT(lilv_nodes_size(port_properties) == 1);
+ lilv_nodes_free(port_properties);
+
+ // Untranslated name (current locale is set to "C" in main)
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_port_get_symbol(plug, p)), "foo"));
+ LilvNode* name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ // Exact language match
+ setenv("LANG", "fr_FR", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "épicerie"));
+ lilv_node_free(name);
+
+ // Exact language match (with charset suffix)
+ setenv("LANG", "fr_CA.utf8", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "dépanneur"));
+ lilv_node_free(name);
+
+ // Partial language match (choose value translated for different country)
+ setenv("LANG", "fr_BE", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT((!strcmp(lilv_node_as_string(name), "dépanneur"))
+ ||(!strcmp(lilv_node_as_string(name), "épicerie")));
+ lilv_node_free(name);
+
+ // Partial language match (choose country-less language tagged value)
+ setenv("LANG", "es_MX", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "tienda"));
+ lilv_node_free(name);
+
+ // No language match (choose untranslated value)
+ setenv("LANG", "cn", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ // Invalid language
+ setenv("LANG", "1!", 1);
+ name = lilv_port_get_name(plug, p);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "store"));
+ lilv_node_free(name);
+
+ setenv("LANG", "en_CA.utf-8", 1);
+
+ // Language tagged value with no untranslated values
+ LilvNode* rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment");
+ LilvNodes* comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)),
+ "comment"));
+ LilvNode* comment = lilv_port_get(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(comment), "comment"));
+ lilv_node_free(comment);
+ lilv_nodes_free(comments);
+
+ setenv("LANG", "fr", 1);
+
+ comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)),
+ "commentaires"));
+ lilv_nodes_free(comments);
+
+ setenv("LANG", "cn", 1);
+
+ comments = lilv_port_get_value(plug, p, rdfs_comment);
+ TEST_ASSERT(!comments);
+ lilv_nodes_free(comments);
+
+ lilv_node_free(rdfs_comment);
+
+ setenv("LANG", "C", 1); // Reset locale
+
+ LilvScalePoints* points = lilv_port_get_scale_points(plug, p);
+ TEST_ASSERT(lilv_scale_points_size(points) == 2);
+
+ LilvIter* sp_iter = lilv_scale_points_begin(points);
+ const LilvScalePoint* sp0 = lilv_scale_points_get(points, sp_iter);
+ TEST_ASSERT(sp0);
+ sp_iter = lilv_scale_points_next(points, sp_iter);
+ const LilvScalePoint* sp1 = lilv_scale_points_get(points, sp_iter);
+ TEST_ASSERT(sp1);
+
+ TEST_ASSERT(
+ ((!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp0)), "Sin")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp0)) == 3)
+ &&
+ (!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp1)), "Cos")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp1)) == 4))
+ ||
+ ((!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp0)), "Cos")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp0)) == 4)
+ &&
+ (!strcmp(lilv_node_as_string(lilv_scale_point_get_label(sp1)), "Sin")
+ && lilv_node_as_float(lilv_scale_point_get_value(sp1)) == 3)));
+
+ LilvNode* homepage_p = lilv_new_uri(world, "http://usefulinc.com/ns/doap#homepage");
+ LilvNodes* homepages = lilv_plugin_get_value(plug, homepage_p);
+ TEST_ASSERT(lilv_nodes_size(homepages) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(homepages)),
+ "http://example.org/someplug"));
+
+ LilvNode *min, *max, *def;
+ lilv_port_get_range(plug, p, &def, &min, &max);
+ TEST_ASSERT(def);
+ TEST_ASSERT(min);
+ TEST_ASSERT(max);
+ TEST_ASSERT(lilv_node_as_float(def) == 0.5);
+ TEST_ASSERT(lilv_node_as_float(min) == -1.0);
+ TEST_ASSERT(lilv_node_as_float(max) == 1.0);
+
+ LilvNode* integer_prop = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#integer");
+ LilvNode* toggled_prop = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#toggled");
+
+ TEST_ASSERT(lilv_port_has_property(plug, p, integer_prop));
+ TEST_ASSERT(!lilv_port_has_property(plug, p, toggled_prop));
+
+ const LilvPort* ep = lilv_plugin_get_port_by_index(plug, 1);
+
+ LilvNode* event_type = lilv_new_uri(world, "http://example.org/event");
+ LilvNode* event_type_2 = lilv_new_uri(world, "http://example.org/otherEvent");
+ LilvNode* atom_event = lilv_new_uri(world, "http://example.org/atomEvent");
+ TEST_ASSERT(lilv_port_supports_event(plug, ep, event_type));
+ TEST_ASSERT(!lilv_port_supports_event(plug, ep, event_type_2));
+ TEST_ASSERT(lilv_port_supports_event(plug, ep, atom_event));
+
+ LilvNode* name_p = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#name");
+ LilvNodes* names = lilv_port_get_value(plug, p, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(names)),
+ "store"));
+ lilv_nodes_free(names);
+
+ LilvNode* true_val = lilv_new_bool(world, true);
+ LilvNode* false_val = lilv_new_bool(world, false);
+
+ TEST_ASSERT(!lilv_node_equals(true_val, false_val));
+
+ lilv_world_set_option(world, LILV_OPTION_FILTER_LANG, false_val);
+ names = lilv_port_get_value(plug, p, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 4);
+ lilv_nodes_free(names);
+ lilv_world_set_option(world, LILV_OPTION_FILTER_LANG, true_val);
+
+ lilv_node_free(false_val);
+ lilv_node_free(true_val);
+
+ names = lilv_port_get_value(plug, ep, name_p);
+ TEST_ASSERT(lilv_nodes_size(names) == 1);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(names)),
+ "Event Input"));
+
+ const LilvPort* ap_in = lilv_plugin_get_port_by_index(plug, 2);
+
+ TEST_ASSERT(lilv_port_is_a(plug, ap_in, in_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_in, out_class));
+ TEST_ASSERT(lilv_port_is_a(plug, ap_in, audio_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_in, control_class));
+
+ const LilvPort* ap_out = lilv_plugin_get_port_by_index(plug, 3);
+
+ TEST_ASSERT(lilv_port_is_a(plug, ap_out, out_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_out, in_class));
+ TEST_ASSERT(lilv_port_is_a(plug, ap_out, audio_class));
+ TEST_ASSERT(!lilv_port_is_a(plug, ap_out, control_class));
+
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, control_class, in_class , NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class , in_class , NULL) == 1);
+ TEST_ASSERT(lilv_plugin_get_num_ports_of_class(plug, audio_class , out_class, NULL) == 1);
+
+ lilv_nodes_free(names);
+ lilv_node_free(name_p);
+
+ lilv_node_free(integer_prop);
+ lilv_node_free(toggled_prop);
+ lilv_node_free(event_type);
+ lilv_node_free(event_type_2);
+ lilv_node_free(atom_event);
+
+ lilv_node_free(min);
+ lilv_node_free(max);
+ lilv_node_free(def);
+
+ lilv_node_free(homepage_p);
+ lilv_nodes_free(homepages);
+
+ lilv_scale_points_free(points);
+ lilv_node_free(control_class);
+ lilv_node_free(audio_class);
+ lilv_node_free(out_class);
+ lilv_node_free(in_class);
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static unsigned
+ui_supported(const char* container_type_uri,
+ const char* ui_type_uri)
+{
+ return !strcmp(container_type_uri, ui_type_uri);
+}
+
+static int
+test_ui(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2UI
+ ":plug a lv2:Plugin ; a lv2:CompressorPlugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "lv2:optionalFeature lv2:hardRTCapable ; "
+ "lv2:requiredFeature <http://lv2plug.in/ns/ext/event> ; "
+ "lv2ui:ui :ui , :ui2 , :ui3 , :ui4 ; "
+ "doap:maintainer [ foaf:name \"David Robillard\" ; "
+ " foaf:homepage <http://drobilla.net> ; foaf:mbox <mailto:d@drobilla.net> ] ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"foo\" ; lv2:name \"bar\" ; "
+ " lv2:minimum -1.0 ; lv2:maximum 1.0 ; lv2:default 0.5 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 1 ; lv2:symbol \"bar\" ; lv2:name \"Baz\" ; "
+ " lv2:minimum -2.0 ; lv2:maximum 2.0 ; lv2:default 1.0 "
+ "] , [ "
+ " a lv2:ControlPort ; a lv2:OutputPort ; "
+ " lv2:index 2 ; lv2:symbol \"latency\" ; lv2:name \"Latency\" ; "
+ " lv2:portProperty lv2:reportsLatency "
+ "] .\n"
+ ":ui a lv2ui:GtkUI ; "
+ " lv2ui:requiredFeature lv2ui:makeResident ; "
+ " lv2ui:binary <ui" SHLIB_EXT "> ; "
+ " lv2ui:optionalFeature lv2ui:ext_presets . "
+ ":ui2 a lv2ui:GtkUI ; lv2ui:binary <ui2" SHLIB_EXT "> . "
+ ":ui3 a lv2ui:GtkUI ; lv2ui:binary <ui3" SHLIB_EXT "> . "
+ ":ui4 a lv2ui:GtkUI ; lv2ui:binary <ui4" SHLIB_EXT "> . ")) {
+ return 0;
+ }
+
+ init_uris();
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ LilvUIs* uis = lilv_plugin_get_uis(plug);
+ TEST_ASSERT(lilv_uis_size(uis) == 4);
+
+ const LilvUI* ui0 = lilv_uis_get(uis, lilv_uis_begin(uis));
+ TEST_ASSERT(ui0);
+
+ LilvNode* ui_uri = lilv_new_uri(world, "http://example.org/ui");
+ LilvNode* ui2_uri = lilv_new_uri(world, "http://example.org/ui3");
+ LilvNode* ui3_uri = lilv_new_uri(world, "http://example.org/ui4");
+ LilvNode* noui_uri = lilv_new_uri(world, "http://example.org/notaui");
+
+ const LilvUI* ui0_2 = lilv_uis_get_by_uri(uis, ui_uri);
+ TEST_ASSERT(ui0 == ui0_2);
+ TEST_ASSERT(lilv_node_equals(lilv_ui_get_uri(ui0_2), ui_uri));
+
+ const LilvUI* ui2 = lilv_uis_get_by_uri(uis, ui2_uri);
+ TEST_ASSERT(ui2 != ui0);
+
+ const LilvUI* ui3 = lilv_uis_get_by_uri(uis, ui3_uri);
+ TEST_ASSERT(ui3 != ui0);
+
+ const LilvUI* noui = lilv_uis_get_by_uri(uis, noui_uri);
+ TEST_ASSERT(noui == NULL);
+
+ const LilvNodes* classes = lilv_ui_get_classes(ui0);
+ TEST_ASSERT(lilv_nodes_size(classes) == 1);
+
+ LilvNode* ui_class_uri = lilv_new_uri(world,
+ "http://lv2plug.in/ns/extensions/ui#GtkUI");
+
+ LilvNode* unknown_ui_class_uri = lilv_new_uri(world,
+ "http://example.org/mysteryUI");
+
+ TEST_ASSERT(lilv_node_equals(lilv_nodes_get_first(classes), ui_class_uri));
+ TEST_ASSERT(lilv_ui_is_a(ui0, ui_class_uri));
+
+ const LilvNode* ui_type = NULL;
+ TEST_ASSERT(lilv_ui_is_supported(ui0, ui_supported, ui_class_uri, &ui_type));
+ TEST_ASSERT(!lilv_ui_is_supported(ui0, ui_supported, unknown_ui_class_uri, &ui_type));
+ TEST_ASSERT(lilv_node_equals(ui_type, ui_class_uri));
+
+ const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug);
+ const LilvNode* ui_bundle_uri = lilv_ui_get_bundle_uri(ui0);
+ TEST_ASSERT(lilv_node_equals(plug_bundle_uri, ui_bundle_uri));
+
+ char* ui_binary_uri_str = (char*)malloc(TEST_PATH_MAX);
+ snprintf(ui_binary_uri_str, TEST_PATH_MAX, "%s%s",
+ lilv_node_as_string(plug_bundle_uri), "ui" SHLIB_EXT);
+
+ const LilvNode* ui_binary_uri = lilv_ui_get_binary_uri(ui0);
+
+ LilvNode* expected_uri = lilv_new_uri(world, ui_binary_uri_str);
+ TEST_ASSERT(lilv_node_equals(expected_uri, ui_binary_uri));
+
+ free(ui_binary_uri_str);
+ lilv_node_free(unknown_ui_class_uri);
+ lilv_node_free(ui_class_uri);
+ lilv_node_free(ui_uri);
+ lilv_node_free(ui2_uri);
+ lilv_node_free(ui3_uri);
+ lilv_node_free(noui_uri);
+ lilv_node_free(expected_uri);
+ lilv_uis_free(uis);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+uint32_t atom_Float = 0;
+float in = 1.0;
+float out = 42.0;
+float control = 1234.0;
+
+static const void*
+get_port_value(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type)
+{
+ if (!strcmp(port_symbol, "input")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &in;
+ } else if (!strcmp(port_symbol, "output")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &out;
+ } else if (!strcmp(port_symbol, "control")) {
+ *size = sizeof(float);
+ *type = atom_Float;
+ return &control;
+ } else {
+ fprintf(stderr, "error: get_port_value for nonexistent port `%s'\n",
+ port_symbol);
+ *size = *type = 0;
+ return NULL;
+ }
+}
+
+static void
+set_port_value(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ if (!strcmp(port_symbol, "input")) {
+ in = *(const float*)value;
+ } else if (!strcmp(port_symbol, "output")) {
+ out = *(const float*)value;
+ } else if (!strcmp(port_symbol, "control")) {
+ control = *(const float*)value;
+ } else {
+ fprintf(stderr, "error: set_port_value for nonexistent port `%s'\n",
+ port_symbol);
+ }
+}
+
+char** uris = NULL;
+size_t n_uris = 0;
+
+static LV2_URID
+map_uri(LV2_URID_Map_Handle handle,
+ const char* uri)
+{
+ for (size_t i = 0; i < n_uris; ++i) {
+ if (!strcmp(uris[i], uri)) {
+ return i + 1;
+ }
+ }
+
+ assert(serd_uri_string_has_scheme((const uint8_t*)uri));
+ uris = (char**)realloc(uris, ++n_uris * sizeof(char*));
+ uris[n_uris - 1] = lilv_strdup(uri);
+ return n_uris;
+}
+
+static const char*
+unmap_uri(LV2_URID_Map_Handle handle,
+ LV2_URID urid)
+{
+ if (urid > 0 && urid <= n_uris) {
+ return uris[urid - 1];
+ }
+ return NULL;
+}
+
+static char* temp_dir = NULL;
+
+static char*
+lilv_make_path(LV2_State_Make_Path_Handle handle,
+ const char* path)
+{
+ return lilv_path_join(temp_dir, path);
+}
+
+static int
+test_state(void)
+{
+ init_world();
+
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(LILV_TEST_BUNDLE);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ LilvNode* plugin_uri = lilv_new_uri(world,
+ "http://example.org/lilv-test-plugin");
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LV2_URID_Map map = { NULL, map_uri };
+ LV2_Feature map_feature = { LV2_URID_MAP_URI, &map };
+ LV2_URID_Unmap unmap = { NULL, unmap_uri };
+ LV2_Feature unmap_feature = { LV2_URID_UNMAP_URI, &unmap };
+ const LV2_Feature* features[] = { &map_feature, &unmap_feature, NULL };
+
+ atom_Float = map.map(map.handle, "http://lv2plug.in/ns/ext/atom#Float");
+
+ LilvNode* num = lilv_new_int(world, 5);
+ LilvState* nostate = lilv_state_new_from_file(world, &map, num, "/junk");
+ TEST_ASSERT(!nostate);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, features);
+ TEST_ASSERT(instance);
+ lilv_instance_activate(instance);
+ lilv_instance_connect_port(instance, 0, &in);
+ lilv_instance_connect_port(instance, 1, &out);
+ lilv_instance_run(instance, 1);
+ TEST_ASSERT(in == 1.0);
+ TEST_ASSERT(out == 1.0);
+
+ temp_dir = lilv_realpath("temp");
+
+ const char* file_dir = NULL;
+ char* copy_dir = NULL;
+ char* link_dir = NULL;
+ char* save_dir = NULL;
+
+ // Get instance state state
+ LilvState* state = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+
+ // Get another instance state
+ LilvState* state2 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+
+ // Ensure they are equal
+ TEST_ASSERT(lilv_state_equals(state, state2));
+
+ // Check that we can't delete unsaved state
+ TEST_ASSERT(lilv_state_delete(world, state));
+
+ // Check that state has no URI
+ TEST_ASSERT(!lilv_state_get_uri(state));
+
+ // Check that we can't save a state with no URI
+ char* bad_state_str = lilv_state_to_string(
+ world, &map, &unmap, state, NULL, NULL);
+ TEST_ASSERT(!bad_state_str);
+
+ // Check that we can't restore the NULL string (and it doesn't crash)
+ LilvState* bad_state = lilv_state_new_from_string(world, &map, NULL);
+ TEST_ASSERT(!bad_state);
+
+ // Save state to a string
+ char* state1_str = lilv_state_to_string(
+ world, &map, &unmap, state, "http://example.org/state1", NULL);
+
+ // Restore from string
+ LilvState* from_str = lilv_state_new_from_string(world, &map, state1_str);
+
+ // Ensure they are equal
+ TEST_ASSERT(lilv_state_equals(state, from_str));
+ free(state1_str);
+
+ const LilvNode* state_plugin_uri = lilv_state_get_plugin_uri(state);
+ TEST_ASSERT(lilv_node_equals(state_plugin_uri, plugin_uri));
+
+ // Tinker with the label of the first state
+ TEST_ASSERT(lilv_state_get_label(state) == NULL);
+ lilv_state_set_label(state, "Test State Old Label");
+ TEST_ASSERT(!strcmp(lilv_state_get_label(state), "Test State Old Label"));
+ lilv_state_set_label(state, "Test State");
+ TEST_ASSERT(!strcmp(lilv_state_get_label(state), "Test State"));
+
+ TEST_ASSERT(!lilv_state_equals(state, state2)); // Label changed
+
+ // Run and get a new instance state (which should now differ)
+ lilv_instance_run(instance, 1);
+ LilvState* state3 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+ TEST_ASSERT(!lilv_state_equals(state2, state3)); // num_runs changed
+
+ // Restore instance state to original state
+ lilv_state_restore(state2, instance, set_port_value, NULL, 0, NULL);
+
+ // Take a new snapshot and ensure it matches the set state
+ LilvState* state4 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, save_dir,
+ get_port_value, world, 0, NULL);
+ TEST_ASSERT(lilv_state_equals(state2, state4));
+
+ // Set some metadata properties
+ lilv_state_set_metadata(state, map.map(map.handle, LILV_NS_RDFS "comment"),
+ "This is a comment",
+ strlen("This is a comment") + 1,
+ map.map(map.handle, "http://lv2plug.in/ns/ext/atom#Literal"),
+ LV2_STATE_IS_POD);
+ lilv_state_set_metadata(state, map.map(map.handle, "http://example.org/metablob"),
+ "LIVEBEEF",
+ strlen("LIVEBEEF") + 1,
+ map.map(map.handle, "http://example.org/MetaBlob"),
+ 0);
+
+ // Save state to a directory
+ int ret = lilv_state_save(world, &map, &unmap, state, NULL,
+ "state/state.lv2", "state.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load state from directory
+ LilvState* state5 = lilv_state_new_from_file(world, &map, NULL,
+ "state/state.lv2/state.ttl");
+
+ TEST_ASSERT(lilv_state_equals(state, state5)); // Round trip accuracy
+ TEST_ASSERT(lilv_state_get_num_properties(state) == 8);
+
+ // Attempt to save state to nowhere (error)
+ ret = lilv_state_save(world, &map, &unmap, state, NULL, NULL, NULL);
+ TEST_ASSERT(ret);
+
+ // Save another state to the same directory (update manifest)
+ ret = lilv_state_save(world, &map, &unmap, state, NULL,
+ "state/state.lv2", "state2.ttl");
+ TEST_ASSERT(!ret);
+
+ // Save state with URI to a directory
+ const char* state_uri = "http://example.org/state";
+ ret = lilv_state_save(world, &map, &unmap, state, state_uri,
+ "state/state6.lv2", "state6.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load default bundle into world and load state from it
+ uint8_t* state6_path = (uint8_t*)lilv_path_absolute("state/state6.lv2/");
+ SerdNode state6_uri = serd_node_new_file_uri(state6_path, 0, 0, true);
+ LilvNode* test_state_bundle = lilv_new_uri(world, (const char*)state6_uri.buf);
+ LilvNode* test_state_node = lilv_new_uri(world, state_uri);
+ lilv_world_load_bundle(world, test_state_bundle);
+ lilv_world_load_resource(world, test_state_node);
+ serd_node_free(&state6_uri);
+ free(state6_path);
+
+ LilvState* state6 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(lilv_state_equals(state, state6)); // Round trip accuracy
+
+ // Check that loaded state has correct URI
+ TEST_ASSERT(lilv_state_get_uri(state6));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_state_get_uri(state6)),
+ state_uri));
+
+ lilv_world_unload_resource(world, test_state_node);
+ lilv_world_unload_bundle(world, test_state_bundle);
+
+ LilvState* state6_2 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(!state6_2); // No longer present
+ lilv_state_free(state6_2);
+
+ lilv_node_free(test_state_bundle);
+ lilv_node_free(test_state_node);
+
+ unsetenv("LV2_STATE_BUNDLE");
+
+ // Make directories and test files support
+ mkdir("temp", 0700);
+ file_dir = temp_dir;
+ mkdir("files", 0700);
+ copy_dir = lilv_realpath("files");
+ mkdir("links", 0700);
+ link_dir = lilv_realpath("links");
+
+ LV2_State_Make_Path make_path = { NULL, lilv_make_path };
+ LV2_Feature make_path_feature = { LV2_STATE__makePath, &make_path };
+ const LV2_Feature* ffeatures[] = { &make_path_feature, &map_feature, NULL };
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+ instance = lilv_plugin_instantiate(plugin, 48000.0, ffeatures);
+ lilv_instance_activate(instance);
+ lilv_instance_connect_port(instance, 0, &in);
+ lilv_instance_connect_port(instance, 1, &out);
+ lilv_instance_run(instance, 1);
+
+ // Test instantiating twice
+ LilvInstance* instance2 = lilv_plugin_instantiate(plugin, 48000.0, ffeatures);
+ if (!instance2) {
+ fatal_error("Failed to create multiple instances of <%s>\n",
+ lilv_node_as_uri(state_plugin_uri));
+ return 0;
+ }
+ lilv_instance_free(instance2);
+
+ // Get instance state state
+ LilvState* fstate = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Get another instance state
+ LilvState* fstate2 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate2.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Should be identical
+ TEST_ASSERT(lilv_state_equals(fstate, fstate2));
+
+ // Run, writing more to rec file
+ lilv_instance_run(instance, 2);
+
+ // Get yet another instance state
+ LilvState* fstate3 = lilv_state_new_from_instance(
+ plugin, instance, &map, file_dir, copy_dir, link_dir, "state/fstate3.lv2",
+ get_port_value, world, 0, ffeatures);
+
+ // Should be different
+ TEST_ASSERT(!lilv_state_equals(fstate, fstate3));
+
+ // Save state to a directory
+ ret = lilv_state_save(world, &map, &unmap, fstate, NULL,
+ "state/fstate.lv2", "fstate.ttl");
+ TEST_ASSERT(!ret);
+
+ // Load state from directory
+ LilvState* fstate4 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate.lv2/fstate.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate, fstate4)); // Round trip accuracy
+
+ // Restore instance state to loaded state
+ lilv_state_restore(fstate4, instance, set_port_value, NULL, 0, ffeatures);
+
+ // Take a new snapshot and ensure it matches
+ LilvState* fstate5 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate5.lv2",
+ get_port_value, world, 0, ffeatures);
+ TEST_ASSERT(lilv_state_equals(fstate3, fstate5));
+
+ // Save state to a (different) directory again
+ ret = lilv_state_save(world, &map, &unmap, fstate, NULL,
+ "state/fstate6.lv2", "fstate6.ttl");
+ TEST_ASSERT(!ret);
+
+ // Reload it and ensure it's identical to the other loaded version
+ LilvState* fstate6 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate6.lv2/fstate6.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate4, fstate6));
+
+ // Run, changing rec file (without changing size)
+ lilv_instance_run(instance, 3);
+
+ // Take a new snapshot
+ LilvState* fstate7 = lilv_state_new_from_instance(
+ plugin, instance, &map,
+ file_dir, copy_dir, link_dir, "state/fstate7.lv2",
+ get_port_value, world, 0, ffeatures);
+ TEST_ASSERT(!lilv_state_equals(fstate6, fstate7));
+
+ // Save the changed state to a (different) directory again
+ ret = lilv_state_save(world, &map, &unmap, fstate7, NULL,
+ "state/fstate7.lv2", "fstate7.ttl");
+ TEST_ASSERT(!ret);
+
+ // Reload it and ensure it's changed
+ LilvState* fstate72 = lilv_state_new_from_file(world, &map, NULL,
+ "state/fstate7.lv2/fstate7.ttl");
+ TEST_ASSERT(lilv_state_equals(fstate72, fstate7));
+ TEST_ASSERT(!lilv_state_equals(fstate6, fstate72));
+
+ // Delete saved state
+ lilv_state_delete(world, fstate7);
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+
+ lilv_node_free(num);
+
+ lilv_state_free(state);
+ lilv_state_free(from_str);
+ lilv_state_free(state2);
+ lilv_state_free(state3);
+ lilv_state_free(state4);
+ lilv_state_free(state5);
+ lilv_state_free(state6);
+ lilv_state_free(fstate);
+ lilv_state_free(fstate2);
+ lilv_state_free(fstate3);
+ lilv_state_free(fstate4);
+ lilv_state_free(fstate5);
+ lilv_state_free(fstate6);
+ lilv_state_free(fstate7);
+ lilv_state_free(fstate72);
+
+ // Free URI map
+ for (size_t i = 0; i < n_uris; ++i) {
+ free(uris[i]);
+ }
+ free(uris);
+ n_uris = 0;
+
+ lilv_node_free(plugin_uri);
+ lilv_node_free(bundle_uri);
+ free(link_dir);
+ free(copy_dir);
+ free(temp_dir);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_bad_port_symbol(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index 0 ; lv2:symbol \"0invalid\" ;"
+ " lv2:name \"Invalid\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+
+ uint32_t n_ports = lilv_plugin_get_num_ports(plug);
+ TEST_ASSERT(n_ports == 0);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_bad_port_index(void)
+{
+ if (!start_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ LICENSE_GPL " ; "
+ "doap:homepage <http://example.org/someplug> ; "
+ "lv2:port [ "
+ " a lv2:ControlPort ; a lv2:InputPort ; "
+ " lv2:index \"notaninteger\" ; lv2:symbol \"invalid\" ;"
+ " lv2:name \"Invalid\" ; "
+ "] .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+
+ uint32_t n_ports = lilv_plugin_get_num_ports(plug);
+ TEST_ASSERT(n_ports == 0);
+
+ cleanup_uris();
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_string(void)
+{
+ char* s = NULL;
+
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo/bar")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo/bar/")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo///bar/")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo///bar//")), "/foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("foo")), ".")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/foo")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("/")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_dirname("//")), "/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b", "/a/")), "b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a", "/b/c/")), "/a")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b/c", "/a/b/d/")), "../c")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_relative_to("/a/b/c", "/a/b/d/e/")), "../../c")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", "b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", "/b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a/", "/b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a/", "b")), "/a/b")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join("/a", NULL)), "/a/")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_path_join(NULL, "/b")), "/b")); free(s);
+
+#ifndef _WIN32
+ setenv("LILV_TEST_1", "test", 1);
+ char* home_foo = lilv_strjoin(getenv("HOME"), "/foo", NULL);
+ TEST_ASSERT(!strcmp((s = lilv_expand("$LILV_TEST_1")), "test")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~")), getenv("HOME"))); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~foo")), "~foo")); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("~/foo")), home_foo)); free(s);
+ TEST_ASSERT(!strcmp((s = lilv_expand("$NOT_A_VAR")), "$NOT_A_VAR")); free(s);
+ free(home_foo);
+ unsetenv("LILV_TEST_1");
+#endif
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_world(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+
+ LilvNode* num = lilv_new_int(world, 4);
+ LilvNode* uri = lilv_new_uri(world, "http://example.org/object");
+
+ LilvNodes* matches = lilv_world_find_nodes(world, num, NULL, NULL);
+ TEST_ASSERT(!matches);
+
+ matches = lilv_world_find_nodes(world, NULL, num, NULL);
+ TEST_ASSERT(!matches);
+
+ matches = lilv_world_find_nodes(world, NULL, uri, NULL);
+ TEST_ASSERT(!matches);
+
+ lilv_node_free(uri);
+ lilv_node_free(num);
+
+ lilv_world_unload_bundle(world, NULL);
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_reload_bundle(void)
+{
+ // Create a simple plugin bundle
+ create_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("First name") " .");
+
+ if (!init_world()) {
+ return 0;
+ }
+
+ init_uris();
+ lilv_world_load_specifications(world);
+
+ // Load bundle
+ LilvNode* bundle_uri = lilv_new_uri(world, bundle_dir_uri);
+ lilv_world_load_bundle(world, bundle_uri);
+
+ // Check that plugin is present
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plug = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug);
+
+ // Check that plugin name is correct
+ LilvNode* name = lilv_plugin_get_name(plug);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name), "First name"));
+ lilv_node_free(name);
+
+ // Unload bundle from world and delete it
+ lilv_world_unload_bundle(world, bundle_uri);
+ delete_bundle();
+
+ // Create a new version of the same bundle, but with a different name
+ create_bundle(MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Second name") " .");
+
+ // Check that plugin is no longer in the world's plugin list
+ TEST_ASSERT(lilv_plugins_size(plugins) == 0);
+
+ // Load new bundle
+ lilv_world_load_bundle(world, bundle_uri);
+
+ // Check that plugin is present again and is the same LilvPlugin
+ const LilvPlugin* plug2 = lilv_plugins_get_by_uri(plugins, plugin_uri_value);
+ TEST_ASSERT(plug2);
+ TEST_ASSERT(plug2 == plug);
+
+ // Check that plugin now has new name
+ LilvNode* name2 = lilv_plugin_get_name(plug2);
+ TEST_ASSERT(name2);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(name2), "Second name"));
+ lilv_node_free(name2);
+
+ // Load new bundle again (noop)
+ lilv_world_load_bundle(world, bundle_uri);
+
+ cleanup_uris();
+ lilv_node_free(bundle_uri);
+ lilv_world_free(world);
+ world = NULL;
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_replace_version(void)
+{
+ if (!init_world()) {
+ return 0;
+ }
+
+ LilvNode* plug_uri = lilv_new_uri(world, "http://example.org/versioned");
+ LilvNode* lv2_minorVersion = lilv_new_uri(world, LV2_CORE__minorVersion);
+ LilvNode* lv2_microVersion = lilv_new_uri(world, LV2_CORE__microVersion);
+ LilvNode* minor = NULL;
+ LilvNode* micro = NULL;
+
+ char* old_bundle_path = lilv_strjoin(LILV_TEST_DIR, "old_version.lv2/", 0);
+
+ // Load plugin from old bundle
+ LilvNode* old_bundle = lilv_new_file_uri(world, NULL, old_bundle_path);
+ lilv_world_load_bundle(world, old_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check version
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* old_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(old_plug);
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "1"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "0"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ char* new_bundle_path = lilv_strjoin(LILV_TEST_DIR, "new_version.lv2/", 0);
+
+ // Load plugin from new bundle
+ LilvNode* new_bundle = lilv_new_file_uri(world, NULL, new_bundle_path);
+ lilv_world_load_bundle(world, new_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check that version in the world model has changed
+ plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* new_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(new_plug);
+ TEST_ASSERT(lilv_node_equals(lilv_plugin_get_bundle_uri(new_plug), new_bundle));
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "2"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "1"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ // Try to load the old version again
+ lilv_world_load_bundle(world, old_bundle);
+ lilv_world_load_resource(world, plug_uri);
+
+ // Check that version in the world model has not changed
+ plugins = lilv_world_get_all_plugins(world);
+ new_plug = lilv_plugins_get_by_uri(plugins, plug_uri);
+ TEST_ASSERT(new_plug);
+ minor = lilv_world_get(world, plug_uri, lv2_minorVersion, 0);
+ micro = lilv_world_get(world, plug_uri, lv2_microVersion, 0);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(minor), "2"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(micro), "1"));
+ lilv_node_free(micro);
+ lilv_node_free(minor);
+
+ lilv_node_free(new_bundle);
+ lilv_node_free(old_bundle);
+ free(new_bundle_path);
+ free(old_bundle_path);
+ lilv_node_free(plug_uri);
+ lilv_node_free(lv2_minorVersion);
+ lilv_node_free(lv2_microVersion);
+ return 1;
+}
+
+/*****************************************************************************/
+
+static int
+test_get_symbol(void)
+{
+ if (!start_bundle(
+ MANIFEST_PREFIXES
+ ":plug a lv2:Plugin ; lv2:symbol \"plugsym\" ; lv2:binary <foo" SHLIB_EXT "> ; rdfs:seeAlso <plugin.ttl> .\n",
+ BUNDLE_PREFIXES PREFIX_LV2EV
+ ":plug a lv2:Plugin ; "
+ PLUGIN_NAME("Test plugin") " ; "
+ "lv2:symbol \"plugsym\" .")) {
+ return 0;
+ }
+
+ init_uris();
+
+ LilvNode* plug_sym = lilv_world_get_symbol(world, plugin_uri_value);
+ LilvNode* path = lilv_new_uri(world, "http://example.org/foo");
+ LilvNode* path_sym = lilv_world_get_symbol(world, path);
+ LilvNode* query = lilv_new_uri(world, "http://example.org/foo?bar=baz");
+ LilvNode* query_sym = lilv_world_get_symbol(world, query);
+ LilvNode* frag = lilv_new_uri(world, "http://example.org/foo#bar");
+ LilvNode* frag_sym = lilv_world_get_symbol(world, frag);
+ LilvNode* queryfrag = lilv_new_uri(world, "http://example.org/foo?bar=baz#quux");
+ LilvNode* queryfrag_sym = lilv_world_get_symbol(world, queryfrag);
+ LilvNode* nonuri = lilv_new_int(world, 42);
+
+ TEST_ASSERT(lilv_world_get_symbol(world, nonuri) == NULL);
+ TEST_ASSERT(!strcmp(lilv_node_as_string(plug_sym), "plugsym"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(path_sym), "foo"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(query_sym), "bar_baz"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(frag_sym), "bar"));
+ TEST_ASSERT(!strcmp(lilv_node_as_string(queryfrag_sym), "quux"));
+
+ lilv_node_free(nonuri);
+ lilv_node_free(queryfrag_sym);
+ lilv_node_free(queryfrag);
+ lilv_node_free(frag_sym);
+ lilv_node_free(frag);
+ lilv_node_free(query_sym);
+ lilv_node_free(query);
+ lilv_node_free(path_sym);
+ lilv_node_free(path);
+ lilv_node_free(plug_sym);
+ cleanup_uris();
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/* add tests here */
+static struct TestCase tests[] = {
+ TEST_CASE(util),
+ TEST_CASE(value),
+ TEST_CASE(verify),
+ TEST_CASE(no_verify),
+ TEST_CASE(discovery),
+ TEST_CASE(lv2_path),
+ TEST_CASE(classes),
+ TEST_CASE(plugin),
+ TEST_CASE(project),
+ TEST_CASE(no_author),
+ TEST_CASE(project_no_author),
+ TEST_CASE(preset),
+ TEST_CASE(prototype),
+ TEST_CASE(port),
+ TEST_CASE(ui),
+ TEST_CASE(bad_port_symbol),
+ TEST_CASE(bad_port_index),
+ TEST_CASE(bad_port_index),
+ TEST_CASE(string),
+ TEST_CASE(world),
+ TEST_CASE(state),
+ TEST_CASE(reload_bundle),
+ TEST_CASE(replace_version),
+ TEST_CASE(get_symbol),
+ { NULL, NULL }
+};
+
+static void
+run_tests(void)
+{
+ int i;
+ for (i = 0; tests[i].title; i++) {
+ printf("*** Test %s\n", tests[i].title);
+ if (!tests[i].func()) {
+ printf("\nTest failed\n");
+ /* test case that wasn't able to be executed at all counts as 1 test + 1 error */
+ error_count++;
+ test_count++;
+ }
+ unload_bundle();
+ cleanup();
+ }
+}
+
+int
+main(int argc, char* argv[])
+{
+ if (argc != 1) {
+ printf("Syntax: %s\n", argv[0]);
+ return 0;
+ }
+ setenv("LANG", "C", 1);
+ init_tests();
+ run_tests();
+ cleanup();
+ printf("\n*** Test Results: %d tests, %d errors\n\n", test_count, error_count);
+ return error_count ? 1 : 0;
+}
diff --git a/test/missing_descriptor.lv2/manifest.ttl.in b/test/missing_descriptor.lv2/manifest.ttl.in
new file mode 100644
index 0000000..789d1ec
--- /dev/null
+++ b/test/missing_descriptor.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-descriptor>
+ a lv2:Plugin ;
+ lv2:binary <missing_descriptor@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_descriptor.ttl> .
diff --git a/test/missing_descriptor.lv2/missing_descriptor.c b/test/missing_descriptor.lv2/missing_descriptor.c
new file mode 100644
index 0000000..0c49f23
--- /dev/null
+++ b/test/missing_descriptor.lv2/missing_descriptor.c
@@ -0,0 +1,21 @@
+/*
+ Lilv Test Plugin - Missing descriptor
+ Copyright 2011-2015 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 "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+LV2_SYMBOL_EXPORT
+const char* msg = "this is not the thing you're looking for";
diff --git a/test/missing_descriptor.lv2/missing_descriptor.ttl.in b/test/missing_descriptor.lv2/missing_descriptor.ttl.in
new file mode 100644
index 0000000..9e2aad8
--- /dev/null
+++ b/test/missing_descriptor.lv2/missing_descriptor.ttl.in
@@ -0,0 +1,38 @@
+# Lilv Test Plugin - Missing descriptor
+# Copyright 2011-2015 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/missing-descriptor>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/missing_descriptor.lv2/test_missing_descriptor.c b/test/missing_descriptor.lv2/test_missing_descriptor.c
new file mode 100644
index 0000000..49909e1
--- /dev/null
+++ b/test/missing_descriptor.lv2/test_missing_descriptor.c
@@ -0,0 +1,46 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-descriptor"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(!instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_name.lv2/manifest.ttl.in b/test/missing_name.lv2/manifest.ttl.in
new file mode 100644
index 0000000..62f4813
--- /dev/null
+++ b/test/missing_name.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-name>
+ a lv2:Plugin ;
+ lv2:binary <missing_name@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_name.ttl> .
diff --git a/test/missing_name.lv2/missing_name.c b/test/missing_name.lv2/missing_name.c
new file mode 100644
index 0000000..6b86e09
--- /dev/null
+++ b/test/missing_name.lv2/missing_name.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Missing name
+ Copyright 2011-2015 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-name"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/missing_name.lv2/missing_name.ttl.in b/test/missing_name.lv2/missing_name.ttl.in
new file mode 100644
index 0000000..68ce23e
--- /dev/null
+++ b/test/missing_name.lv2/missing_name.ttl.in
@@ -0,0 +1,37 @@
+# Lilv Test Plugin - Missing plugin name
+# Copyright 2011-2015 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/missing-name>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/missing_name.lv2/test_missing_name.c b/test/missing_name.lv2/test_missing_name.c
new file mode 100644
index 0000000..960eec0
--- /dev/null
+++ b/test/missing_name.lv2/test_missing_name.c
@@ -0,0 +1,47 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-name"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(instance);
+ lilv_instance_free(instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_plugin.lv2/manifest.ttl.in b/test/missing_plugin.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d969cec
--- /dev/null
+++ b/test/missing_plugin.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-plugin>
+ a lv2:Plugin ;
+ lv2:binary <missing_plugin@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_plugin.ttl> .
diff --git a/test/missing_plugin.lv2/missing_plugin.c b/test/missing_plugin.lv2/missing_plugin.c
new file mode 100644
index 0000000..4d21226
--- /dev/null
+++ b/test/missing_plugin.lv2/missing_plugin.c
@@ -0,0 +1,43 @@
+/*
+ Lilv Test Plugin - Missing plugin
+ Copyright 2011-2015 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-plugin"
+
+static const LV2_Descriptor descriptor = {
+ "http://example.org/not-the-plugin-you-are-looking-for",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ if (index == 0) {
+ return &descriptor;
+ }
+
+ return NULL;
+}
diff --git a/test/missing_plugin.lv2/missing_plugin.ttl.in b/test/missing_plugin.lv2/missing_plugin.ttl.in
new file mode 100644
index 0000000..ed8a7f3
--- /dev/null
+++ b/test/missing_plugin.lv2/missing_plugin.ttl.in
@@ -0,0 +1,38 @@
+# Lilv Test Plugin - Missing plugin
+# Copyright 2011-2015 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/missing-plugin>
+ a lv2:Plugin ;
+ doap:name "Missing descriptor test" ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ 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"
+ ] .
diff --git a/test/missing_plugin.lv2/test_missing_plugin.c b/test/missing_plugin.lv2/test_missing_plugin.c
new file mode 100644
index 0000000..bfc695b
--- /dev/null
+++ b/test/missing_plugin.lv2/test_missing_plugin.c
@@ -0,0 +1,46 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-plugin"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL);
+ TEST_ASSERT(!instance);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_port.lv2/manifest.ttl.in b/test/missing_port.lv2/manifest.ttl.in
new file mode 100644
index 0000000..c090042
--- /dev/null
+++ b/test/missing_port.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-port>
+ a lv2:Plugin ;
+ lv2:binary <missing_port@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_port.ttl> .
diff --git a/test/missing_port.lv2/missing_port.c b/test/missing_port.lv2/missing_port.c
new file mode 100644
index 0000000..dd7e9ff
--- /dev/null
+++ b/test/missing_port.lv2/missing_port.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Missing port
+ Copyright 2011-2016 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-port"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/missing_port.lv2/missing_port.ttl.in b/test/missing_port.lv2/missing_port.ttl.in
new file mode 100644
index 0000000..0dec1cf
--- /dev/null
+++ b/test/missing_port.lv2/missing_port.ttl.in
@@ -0,0 +1,31 @@
+# Lilv Test Plugin - Missing plugin port
+# Copyright 2011-2016 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/missing-port>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:OutputPort ,
+ lv2:ControlPort ;
+ lv2:index 1 ;
+ lv2:symbol "output" ;
+ lv2:name "Output"
+ ] .
diff --git a/test/missing_port.lv2/test_missing_port.c b/test/missing_port.lv2/test_missing_port.c
new file mode 100644
index 0000000..c67a1b8
--- /dev/null
+++ b/test/missing_port.lv2/test_missing_port.c
@@ -0,0 +1,45 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-port"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+
+ // Check that all ports are ignored
+ TEST_ASSERT(lilv_plugin_get_num_ports(plugin) == 0);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/missing_port_name.lv2/manifest.ttl.in b/test/missing_port_name.lv2/manifest.ttl.in
new file mode 100644
index 0000000..d6a4e39
--- /dev/null
+++ b/test/missing_port_name.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/missing-port-name>
+ a lv2:Plugin ;
+ lv2:binary <missing_port_name@SHLIB_EXT@> ;
+ rdfs:seeAlso <missing_port_name.ttl> .
diff --git a/test/missing_port_name.lv2/missing_port_name.c b/test/missing_port_name.lv2/missing_port_name.c
new file mode 100644
index 0000000..4ed44ed
--- /dev/null
+++ b/test/missing_port_name.lv2/missing_port_name.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Missing port name
+ Copyright 2011-2015 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/missing-port-name"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/missing_port_name.lv2/missing_port_name.ttl.in b/test/missing_port_name.lv2/missing_port_name.ttl.in
new file mode 100644
index 0000000..5a58a80
--- /dev/null
+++ b/test/missing_port_name.lv2/missing_port_name.ttl.in
@@ -0,0 +1,30 @@
+# Lilv Test Plugin - Missing port name
+# Copyright 2011-2015 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/missing-port-name>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:port [
+ a lv2:InputPort ,
+ lv2:ControlPort ;
+ lv2:index 0 ;
+ lv2:symbol "input" ;
+ ] . \ No newline at end of file
diff --git a/test/missing_port_name.lv2/test_missing_port_name.c b/test/missing_port_name.lv2/test_missing_port_name.c
new file mode 100644
index 0000000..c000247
--- /dev/null
+++ b/test/missing_port_name.lv2/test_missing_port_name.c
@@ -0,0 +1,49 @@
+#include "lilv/lilv.h"
+#include "../src/lilv_internal.h"
+
+#define PLUGIN_URI "http://example.org/missing-port-name"
+
+#define TEST_ASSERT(check) do {\
+ if (!(check)) {\
+ fprintf(stderr, "%s:%d: failed test: %s\n", __FILE__, __LINE__, #check);\
+ return 1;\
+ }\
+} while (0)
+
+int
+main(int argc, char** argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "USAGE: %s BUNDLE\n", argv[0]);
+ return 1;
+ }
+
+ const char* bundle_path = argv[1];
+ LilvWorld* world = lilv_world_new();
+
+ // Load test plugin bundle
+ uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(bundle_path);
+ SerdNode bundle = serd_node_new_file_uri(abs_bundle, 0, 0, true);
+ LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf);
+ lilv_world_load_bundle(world, bundle_uri);
+ free(abs_bundle);
+ serd_node_free(&bundle);
+ lilv_node_free(bundle_uri);
+
+ LilvNode* plugin_uri = lilv_new_uri(world, PLUGIN_URI);
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ TEST_ASSERT(plugin);
+
+ const LilvPort* port = lilv_plugin_get_port_by_index(plugin, 0);
+ TEST_ASSERT(port);
+ LilvNode* name = lilv_port_get_name(plugin, port);
+ TEST_ASSERT(!name);
+ lilv_node_free(name);
+
+ lilv_node_free(plugin_uri);
+ lilv_world_free(world);
+
+ return 0;
+}
+
diff --git a/test/new_version.lv2/manifest.ttl.in b/test/new_version.lv2/manifest.ttl.in
new file mode 100644
index 0000000..e76f7cf
--- /dev/null
+++ b/test/new_version.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/versioned>
+ a lv2:Plugin ;
+ lv2:binary <new_version@SHLIB_EXT@> ;
+ rdfs:seeAlso <new_version.ttl> .
diff --git a/test/new_version.lv2/new_version.c b/test/new_version.lv2/new_version.c
new file mode 100644
index 0000000..37d9fbf
--- /dev/null
+++ b/test/new_version.lv2/new_version.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - New version
+ Copyright 2011-2016 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/versioned"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/new_version.lv2/new_version.ttl.in b/test/new_version.lv2/new_version.ttl.in
new file mode 100644
index 0000000..7994666
--- /dev/null
+++ b/test/new_version.lv2/new_version.ttl.in
@@ -0,0 +1,40 @@
+# Lilv Test Plugin - New version
+# Copyright 2011-2016 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/versioned>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ doap:name "New version" ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:minorVersion 2 ;
+ lv2:microVersion 1 ;
+ 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"
+ ] .
diff --git a/test/old_version.lv2/manifest.ttl.in b/test/old_version.lv2/manifest.ttl.in
new file mode 100644
index 0000000..3c96cb5
--- /dev/null
+++ b/test/old_version.lv2/manifest.ttl.in
@@ -0,0 +1,7 @@
+@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+
+<http://example.org/versioned>
+ a lv2:Plugin ;
+ lv2:binary <old_version@SHLIB_EXT@> ;
+ rdfs:seeAlso <old_version.ttl> .
diff --git a/test/old_version.lv2/old_version.c b/test/old_version.lv2/old_version.c
new file mode 100644
index 0000000..303f09c
--- /dev/null
+++ b/test/old_version.lv2/old_version.c
@@ -0,0 +1,93 @@
+/*
+ Lilv Test Plugin - Old version
+ Copyright 2011-2016 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 <stdlib.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define PLUGIN_URI "http://example.org/versioned"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1
+};
+
+typedef struct {
+ float* input;
+ float* output;
+} Test;
+
+static void
+cleanup(LV2_Handle instance)
+{
+ free((Test*)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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ return (LV2_Handle)test;
+}
+
+static void
+run(LV2_Handle instance, uint32_t sample_count)
+{
+ Test* test = (Test*)instance;
+
+ *test->output = *test->input;
+}
+
+static const LV2_Descriptor descriptor = {
+ PLUGIN_URI,
+ instantiate,
+ connect_port,
+ NULL, // activate,
+ run,
+ NULL, // deactivate,
+ cleanup,
+ NULL // extension_data
+};
+
+LV2_SYMBOL_EXPORT
+const LV2_Descriptor* lv2_descriptor(uint32_t index)
+{
+ return (index == 0) ? &descriptor : NULL;
+}
diff --git a/test/old_version.lv2/old_version.ttl.in b/test/old_version.lv2/old_version.ttl.in
new file mode 100644
index 0000000..2b68f76
--- /dev/null
+++ b/test/old_version.lv2/old_version.ttl.in
@@ -0,0 +1,40 @@
+# Lilv Test Plugin - Old version
+# Copyright 2011-2016 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/versioned>
+ a lv2:Plugin ;
+ doap:license <http://opensource.org/licenses/isc> ;
+ doap:name "Old version" ;
+ lv2:optionalFeature lv2:hardRTCapable ;
+ lv2:minorVersion 1 ;
+ lv2:microVersion 0 ;
+ 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"
+ ] .
diff --git a/test/test.lv2/manifest.ttl.in b/test/test.lv2/manifest.ttl.in
new file mode 100644
index 0000000..bc3952c
--- /dev/null
+++ b/test/test.lv2/manifest.ttl.in
@@ -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@SHLIB_EXT@> ;
+ rdfs:seeAlso <test.ttl> .
diff --git a/test/test.lv2/test.c b/test/test.lv2/test.c
new file mode 100644
index 0000000..d16e4a9
--- /dev/null
+++ b/test/test.lv2/test.c
@@ -0,0 +1,394 @@
+/*
+ Lilv Test Plugin
+ Copyright 2011-2017 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.
+*/
+
+#define _POSIX_C_SOURCE 200809L
+
+#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"
+
+#ifdef _WIN32
+# include <io.h>
+# define mkstemp(pat) _mktemp(pat)
+#endif
+
+#define TEST_URI "http://example.org/lilv-test-plugin"
+
+#define TMP_TEMPLATE "lilv_testXXXXXX"
+
+enum {
+ TEST_INPUT = 0,
+ TEST_OUTPUT = 1,
+ TEST_CONTROL = 2
+};
+
+typedef struct {
+ LV2_URID_Map* map;
+
+ struct {
+ LV2_URID atom_Float;
+ } uris;
+
+ char tmp_file_path[sizeof(TMP_TEMPLATE)];
+ 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->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;
+ case TEST_CONTROL:
+ 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*)calloc(1, sizeof(Test));
+ if (!test) {
+ return NULL;
+ }
+
+ strncpy(test->tmp_file_path, TMP_TEMPLATE, strlen(TMP_TEMPLATE) + 1);
+ mkstemp(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_POD);
+
+ 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_POD);
+
+ 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_POD);
+ 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 (valflags != LV2_STATE_IS_POD) {
+ fprintf(stderr, "error: Restored bad file flags\n");
+ return LV2_STATE_ERR_BAD_FLAGS;
+ }
+
+ 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;
+ }
+}
diff --git a/test/test.lv2/test.ttl.in b/test/test.lv2/test.ttl.in
new file mode 100644
index 0000000..1c16b4c
--- /dev/null
+++ b/test/test.lv2/test.ttl.in
@@ -0,0 +1,46 @@
+# Lilv Test Plugin
+# Copyright 2011-2015 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"
+ ] , [
+ a lv2:InputPort ,
+ lv2:ControlPort ;
+ lv2:index 2 ;
+ lv2:symbol "control" ;
+ lv2:name "Control"
+ ] .
diff --git a/utils/bench.h b/utils/bench.h
new file mode 100644
index 0000000..83d7853
--- /dev/null
+++ b/utils/bench.h
@@ -0,0 +1,52 @@
+/*
+ Copyright 2011-2014 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 200809L
+
+#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(void)
+{
+ 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 */
diff --git a/utils/lilv-bench.c b/utils/lilv-bench.c
new file mode 100644
index 0000000..c0355df
--- /dev/null
+++ b/utils/lilv-bench.c
@@ -0,0 +1,38 @@
+/*
+ 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 <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;
+}
diff --git a/utils/lilv.bash_completion b/utils/lilv.bash_completion
new file mode 100644
index 0000000..4a553a7
--- /dev/null
+++ b/utils/lilv.bash_completion
@@ -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
diff --git a/utils/lv2apply.c b/utils/lv2apply.c
new file mode 100644
index 0000000..66a8c3a
--- /dev/null
+++ b/utils/lv2apply.c
@@ -0,0 +1,354 @@
+/*
+ Copyright 2007-2016 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 <math.h>
+#include <sndfile.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lilv/lilv.h"
+
+/** Control port value set from the command line */
+typedef struct Param {
+ const char* sym; ///< Port symbol
+ float value; ///< Control value
+} Param;
+
+/** Port type (only float ports are supported) */
+typedef enum {
+ TYPE_CONTROL,
+ TYPE_AUDIO
+} PortType;
+
+/** Runtime port information */
+typedef struct {
+ const LilvPort* lilv_port; ///< Port description
+ PortType type; ///< Datatype
+ uint32_t index; ///< Port index
+ float value; ///< Control value (if applicable)
+ bool is_input; ///< True iff an input port
+ bool optional; ///< True iff connection optional
+} Port;
+
+/** Application state */
+typedef struct {
+ LilvWorld* world;
+ const LilvPlugin* plugin;
+ LilvInstance* instance;
+ const char* in_path;
+ const char* out_path;
+ SNDFILE* in_file;
+ SNDFILE* out_file;
+ unsigned n_params;
+ Param* params;
+ unsigned n_ports;
+ unsigned n_audio_in;
+ unsigned n_audio_out;
+ Port* ports;
+} LV2Apply;
+
+static int fatal(LV2Apply* self, int status, const char* fmt, ...);
+
+/** Open a sound file with error handling. */
+static SNDFILE*
+sopen(LV2Apply* self, const char* path, int mode, SF_INFO* fmt)
+{
+ SNDFILE* file = sf_open(path, mode, fmt);
+ const int st = sf_error(file);
+ if (st) {
+ fatal(self, 1, "Failed to open %s (%s)\n", path, sf_error_number(st));
+ return NULL;
+ }
+ return file;
+}
+
+/** Close a sound file with error handling. */
+static void
+sclose(const char* path, SNDFILE* file)
+{
+ int st;
+ if (file && (st = sf_close(file))) {
+ fatal(NULL, 1, "Failed to close %s (%s)\n", path, sf_error_number(st));
+ }
+}
+
+/**
+ Read a single frame from a file into an interleaved buffer.
+
+ If more channels are required than are available in the file, the remaining
+ channels are distributed in a round-robin fashion (LRLRL).
+*/
+static bool
+sread(SNDFILE* file, unsigned file_chans, float* buf, unsigned buf_chans)
+{
+ const sf_count_t n_read = sf_readf_float(file, buf, 1);
+ for (unsigned i = file_chans - 1; i < buf_chans; ++i) {
+ buf[i] = buf[i % file_chans];
+ }
+ return n_read == 1;
+}
+
+/** Clean up all resources. */
+static int
+cleanup(int status, LV2Apply* self)
+{
+ sclose(self->in_path, self->in_file);
+ sclose(self->out_path, self->out_file);
+ lilv_instance_free(self->instance);
+ lilv_world_free(self->world);
+ free(self->ports);
+ free(self->params);
+ return status;
+}
+
+/** Print a fatal error and clean up for exit. */
+static int
+fatal(LV2Apply* self, int status, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ return self ? cleanup(status, self) : status;
+}
+
+/**
+ Create port structures from data (via create_port()) for all ports.
+*/
+static int
+create_ports(LV2Apply* self)
+{
+ LilvWorld* world = self->world;
+ const uint32_t n_ports = lilv_plugin_get_num_ports(self->plugin);
+
+ self->n_ports = n_ports;
+ self->ports = (Port*)calloc(self->n_ports, sizeof(Port));
+
+ /* Get default values for all ports */
+ float* values = (float*)calloc(n_ports, sizeof(float));
+ lilv_plugin_get_port_ranges_float(self->plugin, NULL, NULL, values);
+
+ LilvNode* lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort);
+ LilvNode* lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort);
+ LilvNode* lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort);
+ LilvNode* lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
+ LilvNode* lv2_connectionOptional = lilv_new_uri(world, LV2_CORE__connectionOptional);
+
+ for (uint32_t i = 0; i < n_ports; ++i) {
+ Port* port = &self->ports[i];
+ const LilvPort* lport = lilv_plugin_get_port_by_index(self->plugin, i);
+
+ port->lilv_port = lport;
+ port->index = i;
+ port->value = isnan(values[i]) ? 0.0f : values[i];
+ port->optional = lilv_port_has_property(
+ self->plugin, lport, lv2_connectionOptional);
+
+ /* Check if port is an input or output */
+ if (lilv_port_is_a(self->plugin, lport, lv2_InputPort)) {
+ port->is_input = true;
+ } else if (!lilv_port_is_a(self->plugin, lport, lv2_OutputPort) &&
+ !port->optional) {
+ return fatal(self, 1, "Port %d is neither input nor output\n", i);
+ }
+
+ /* Check if port is an audio or control port */
+ if (lilv_port_is_a(self->plugin, lport, lv2_ControlPort)) {
+ port->type = TYPE_CONTROL;
+ } else if (lilv_port_is_a(self->plugin, lport, lv2_AudioPort)) {
+ port->type = TYPE_AUDIO;
+ if (port->is_input) {
+ ++self->n_audio_in;
+ } else {
+ ++self->n_audio_out;
+ }
+ } else if (!port->optional) {
+ return fatal(self, 1, "Port %d has unsupported type\n", i);
+ }
+ }
+
+ lilv_node_free(lv2_connectionOptional);
+ lilv_node_free(lv2_ControlPort);
+ lilv_node_free(lv2_AudioPort);
+ lilv_node_free(lv2_OutputPort);
+ lilv_node_free(lv2_InputPort);
+ free(values);
+
+ return 0;
+}
+
+static void
+print_version(void)
+{
+ printf(
+ "lv2apply (lilv) " LILV_VERSION "\n"
+ "Copyright 2007-2016 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 int
+print_usage(int status)
+{
+ fprintf(status ? stderr : stdout,
+ "Usage: lv2apply [OPTION]... PLUGIN_URI\n"
+ "Apply an LV2 plugin to an audio file.\n\n"
+ " -i IN_FILE Input file\n"
+ " -o OUT_FILE Output file\n"
+ " -c SYM VAL Control value\n"
+ " --help Display this help and exit\n"
+ " --version Display version information and exit\n");
+ return status;
+}
+
+int
+main(int argc, char** argv)
+{
+ LV2Apply self = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, 0, 0, NULL
+ };
+
+ /* Parse command line arguments */
+ const char* plugin_uri = NULL;
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--version")) {
+ free(self.params);
+ print_version();
+ return 0;
+ } else if (!strcmp(argv[i], "--help")) {
+ free(self.params);
+ return print_usage(0);
+ } else if (!strcmp(argv[i], "-i")) {
+ self.in_path = argv[++i];
+ } else if (!strcmp(argv[i], "-o")) {
+ self.out_path = argv[++i];
+ } else if (!strcmp(argv[i], "-c")) {
+ if (argc < i + 3) {
+ return fatal(&self, 1, "Missing argument for -c\n");
+ }
+ self.params = (Param*)realloc(self.params,
+ ++self.n_params * sizeof(Param));
+ self.params[self.n_params - 1].sym = argv[++i];
+ self.params[self.n_params - 1].value = atof(argv[++i]);
+ } else if (argv[i][0] == '-') {
+ free(self.params);
+ return print_usage(1);
+ } else if (i == argc - 1) {
+ plugin_uri = argv[i];
+ }
+ }
+
+ /* Check that required arguments are given */
+ if (!self.in_path || !self.out_path || !plugin_uri) {
+ free(self.params);
+ return print_usage(1);
+ }
+
+ /* Create world and plugin URI */
+ self.world = lilv_world_new();
+ LilvNode* uri = lilv_new_uri(self.world, plugin_uri);
+ if (!uri) {
+ return fatal(&self, 2, "Invalid plugin URI <%s>\n", plugin_uri);
+ }
+
+ /* Discover world */
+ lilv_world_load_all(self.world);
+
+ /* Get plugin */
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(self.world);
+ const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, uri);
+ lilv_node_free(uri);
+ if (!(self.plugin = plugin)) {
+ return fatal(&self, 3, "Plugin <%s> not found\n", plugin_uri);
+ }
+
+ /* Open input file */
+ SF_INFO in_fmt = { 0, 0, 0, 0, 0, 0 };
+ if (!(self.in_file = sopen(&self, self.in_path, SFM_READ, &in_fmt))) {
+ return 4;
+ }
+
+ /* Create port structures */
+ if (create_ports(&self)) {
+ return 5;
+ }
+
+ if (self.n_audio_in == 0 ||
+ (in_fmt.channels != (int)self.n_audio_in && in_fmt.channels != 1)) {
+ return fatal(&self, 6, "Unable to map %d inputs to %d ports\n",
+ in_fmt.channels, self.n_audio_in);
+ }
+
+ /* Set control values */
+ for (unsigned i = 0; i < self.n_params; ++i) {
+ const Param* param = &self.params[i];
+ LilvNode* sym = lilv_new_string(self.world, param->sym);
+ const LilvPort* port = lilv_plugin_get_port_by_symbol(plugin, sym);
+ lilv_node_free(sym);
+ if (!port) {
+ return fatal(&self, 7, "Unknown port `%s'\n", param->sym);
+ }
+
+ self.ports[lilv_port_get_index(plugin, port)].value = param->value;
+ }
+
+ /* Open output file */
+ SF_INFO out_fmt = in_fmt;
+ out_fmt.channels = self.n_audio_out;
+ if (!(self.out_file = sopen(&self, self.out_path, SFM_WRITE, &out_fmt))) {
+ return 8;
+ }
+
+ /* Instantiate plugin and connect ports */
+ const uint32_t n_ports = lilv_plugin_get_num_ports(plugin);
+ float in_buf[self.n_audio_in];
+ float out_buf[self.n_audio_out];
+ self.instance = lilv_plugin_instantiate(
+ self.plugin, in_fmt.samplerate, NULL);
+ for (uint32_t p = 0, i = 0, o = 0; p < n_ports; ++p) {
+ if (self.ports[p].type == TYPE_CONTROL) {
+ lilv_instance_connect_port(self.instance, p, &self.ports[p].value);
+ } else if (self.ports[p].type == TYPE_AUDIO) {
+ if (self.ports[p].is_input) {
+ lilv_instance_connect_port(self.instance, p, in_buf + i++);
+ } else {
+ lilv_instance_connect_port(self.instance, p, out_buf + o++);
+ }
+ } else {
+ lilv_instance_connect_port(self.instance, p, NULL);
+ }
+ }
+
+ /* Ports are now connected to buffers in interleaved format, so we can run
+ a single frame at a time and avoid having to interleave buffers to
+ read/write from/to sndfile. */
+
+ lilv_instance_activate(self.instance);
+ while (sread(self.in_file, in_fmt.channels, in_buf, self.n_audio_in)) {
+ lilv_instance_run(self.instance, 1);
+ if (sf_writef_float(self.out_file, out_buf, 1) != 1) {
+ return fatal(&self, 9, "Failed to write to output file\n");
+ }
+ }
+ lilv_instance_deactivate(self.instance);
+
+ return cleanup(0, &self);
+}
diff --git a/utils/lv2bench.c b/utils/lv2bench.c
new file mode 100644
index 0000000..e3eb57f
--- /dev/null
+++ b/utils/lv2bench.c
@@ -0,0 +1,268 @@
+/*
+ Copyright 2012-2017 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 200809L
+
+#include <math.h>
+#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-2017 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] [PLUGIN_URI]\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;
+ }
+
+ const size_t atom_capacity = 1024;
+
+ LV2_Atom_Sequence seq_in = {
+ { sizeof(LV2_Atom_Sequence_Body),
+ uri_table_map(&uri_table, LV2_ATOM__Sequence) },
+ { 0, 0 } };
+
+ LV2_Atom_Sequence* seq_out = (LV2_Atom_Sequence*)malloc(
+ sizeof(LV2_Atom_Sequence) + atom_capacity);
+
+ 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);
+ uri_table_destroy(&uri_table);
+ 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);
+ uri_table_destroy(&uri_table);
+ return 0.0;
+ }
+
+ const uint32_t n_ports = lilv_plugin_get_num_ports(p);
+ float* const mins = (float*)calloc(n_ports, sizeof(float));
+ float* const maxes = (float*)calloc(n_ports, sizeof(float));
+ float* const controls = (float*)calloc(n_ports, sizeof(float));
+ lilv_plugin_get_port_ranges_float(p, mins, maxes, controls);
+
+ 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)) {
+ if (isnan(controls[index])) {
+ if (!isnan(mins[index])) {
+ controls[index] = mins[index];
+ } else if (!isnan(maxes[index])) {
+ controls[index] = maxes[index];
+ } else {
+ controls[index] = 0.0;
+ }
+ }
+ 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(seq_out);
+ free(buf);
+ free(controls);
+ uri_table_destroy(&uri_table);
+ return 0.0;
+ }
+ } else if (lilv_port_is_a(p, port, atom_AtomPort)) {
+ if (lilv_port_is_a(p, port, lv2_InputPort)) {
+ lilv_instance_connect_port(instance, index, &seq_in);
+ } else {
+ lilv_instance_connect_port(instance, index, seq_out);
+ }
+ } else {
+ fprintf(stderr, "<%s> port %d has unknown type, skipping\n",
+ uri, index);
+ lilv_instance_free(instance);
+ free(seq_out);
+ free(buf);
+ free(controls);
+ uri_table_destroy(&uri_table);
+ return 0.0;
+ }
+ }
+
+ lilv_instance_activate(instance);
+
+ struct timespec ts = bench_start();
+ for (uint32_t i = 0; i < (sample_count / block_size); ++i) {
+ seq_in.atom.size = sizeof(LV2_Atom_Sequence_Body);
+ seq_in.atom.type = uri_table_map(&uri_table, LV2_ATOM__Sequence);
+ seq_out->atom.size = atom_capacity;
+ seq_out->atom.type = uri_table_map(&uri_table, LV2_ATOM__Chunk);
+
+ lilv_instance_run(instance, block_size);
+ }
+ const double elapsed = bench_end(&ts);
+
+ lilv_instance_deactivate(instance);
+ lilv_instance_free(instance);
+ free(seq_out);
+
+ 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);
+
+ int a = 1;
+ for (; a < argc; ++a) {
+ if (!strcmp(argv[a], "--version")) {
+ print_version();
+ return 0;
+ } else if (!strcmp(argv[a], "--help")) {
+ print_usage();
+ return 0;
+ } else if (!strcmp(argv[a], "-f")) {
+ full_output = true;
+ } else if (!strcmp(argv[a], "-n") && (a + 1 < argc)) {
+ sample_count = atoi(argv[++a]);
+ } else if (!strcmp(argv[a], "-b") && (a + 1 < argc)) {
+ block_size = atoi(argv[++a]);
+ } else if (argv[a][0] != '-') {
+ break;
+ } else {
+ print_usage();
+ return 1;
+ }
+ }
+
+ const char* const plugin_uri_str = (a < argc ? argv[a++] : NULL);
+
+ 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);
+ if (plugin_uri_str) {
+ LilvNode* uri = lilv_new_uri(world, plugin_uri_str);
+ bench(lilv_plugins_get_by_uri(plugins, uri), sample_count, block_size);
+ } else {
+ 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;
+}
diff --git a/utils/lv2info.c b/utils/lv2info.c
new file mode 100644
index 0000000..75a093f
--- /dev/null
+++ b/utils/lv2info.c
@@ -0,0 +1,460 @@
+/*
+ Copyright 2007-2014 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(maxes[index])) {
+ printf("\t\tMaximum: %f\n", maxes[index]);
+ }
+ if (!isnan(defaults[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-2014 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_file_uri(world, NULL, plugin_file);
+
+ FILE* plugin_fd = fopen(plugin_file, "a");
+ if (plugin_fd) {
+ lilv_plugin_write_description(world, p, base, plugin_fd);
+ fclose(plugin_fd);
+ } else {
+ fprintf(stderr, "error: Failed to open %s\n", plugin_file);
+ }
+
+ if (manifest_file) {
+ FILE* manifest_fd = fopen(manifest_file, "a");
+ if (manifest_fd) {
+ lilv_plugin_write_manifest_entry(
+ world, p, base, manifest_fd, plugin_file);
+ fclose(manifest_fd);
+ } else {
+ fprintf(stderr, "error: Failed to open %s\n", manifest_file);
+ }
+ }
+ 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;
+}
+
diff --git a/utils/lv2ls.c b/utils/lv2ls.c
new file mode 100644
index 0000000..1f007f1
--- /dev/null
+++ b/utils/lv2ls.c
@@ -0,0 +1,93 @@
+/*
+ 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 <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-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("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;
+}
diff --git a/utils/uri_table.h b/utils/uri_table.h
new file mode 100644
index 0000000..880d560
--- /dev/null
+++ b/utils/uri_table.h
@@ -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] = (char*)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 */
diff --git a/waf b/waf
index e22930a..a740a95 100755
--- a/waf
+++ b/waf
@@ -1,16 +1,169 @@
#!/usr/bin/env python
+# encoding: latin-1
+# Thomas Nagy, 2005-2018
+#
+"""
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
-# Minimal waf script for projects that include waflib directly
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
-from waflib import Context, Scripting
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
-import inspect
-import os
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
-def main():
- script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
- project_path = os.path.dirname(script_path)
- Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import os, sys, inspect
+
+VERSION="2.0.9"
+REVISION="7ea8f0d5002fd7cec19aa8d10c4784ce"
+GIT="x"
+INSTALL=''
+C1='#W'
+C2='#3'
+C3='#0'
+cwd = os.getcwd()
+join = os.path.join
+
+
+WAF='waf'
+def b(x):
+ return x
+if sys.hexversion>0x300000f:
+ WAF='waf3'
+ def b(x):
+ return x.encode()
+
+def err(m):
+ print(('\033[91mError: %s\033[0m' % m))
+ sys.exit(1)
+
+def unpack_wafdir(dir, src):
+ f = open(src,'rb')
+ c = 'corrupt archive (%d)'
+ while 1:
+ line = f.readline()
+ if not line: err('run waf-light from a folder containing waflib')
+ if line == b('#==>\n'):
+ txt = f.readline()
+ if not txt: err(c % 1)
+ if f.readline() != b('#<==\n'): err(c % 2)
+ break
+ if not txt: err(c % 3)
+ txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00'))
+
+ import shutil, tarfile
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ try:
+ for x in ('Tools', 'extras'):
+ os.makedirs(join(dir, 'waflib', x))
+ except OSError:
+ err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir)
+
+ os.chdir(dir)
+ tmp = 't.bz2'
+ t = open(tmp,'wb')
+ try: t.write(txt)
+ finally: t.close()
+
+ try:
+ t = tarfile.open(tmp)
+ except:
+ try:
+ os.system('bunzip2 t.bz2')
+ t = tarfile.open('t')
+ tmp = 't'
+ except:
+ os.chdir(cwd)
+ try: shutil.rmtree(dir)
+ except OSError: pass
+ err("Waf cannot be unpacked, check that bzip2 support is present")
+
+ try:
+ for x in t: t.extract(x)
+ finally:
+ t.close()
+
+ for x in ('Tools', 'extras'):
+ os.chmod(join('waflib',x), 493)
+
+ if sys.hexversion<0x300000f:
+ sys.path = [join(dir, 'waflib')] + sys.path
+ import fixpy2
+ fixpy2.fixdir(dir)
+
+ os.remove(tmp)
+ os.chdir(cwd)
+
+ try: dir = unicode(dir, 'mbcs')
+ except: pass
+ try:
+ from ctypes import windll
+ windll.kernel32.SetFileAttributesW(dir, 2)
+ except:
+ pass
+
+def test(dir):
+ try:
+ os.stat(join(dir, 'waflib'))
+ return os.path.abspath(dir)
+ except OSError:
+ pass
+
+def find_lib():
+ src = os.path.abspath(inspect.getfile(inspect.getmodule(err)))
+ base, name = os.path.split(src)
+
+ #devs use $WAFDIR
+ w=test(os.environ.get('WAFDIR', ''))
+ if w: return w
+
+ #waf-light
+ if name.endswith('waf-light'):
+ w = test(base)
+ if w: return w
+ err('waf-light requires waflib -> export WAFDIR=/folder')
+
+ dirname = '%s-%s-%s' % (WAF, VERSION, REVISION)
+ for i in (INSTALL,'/usr','/usr/local','/opt'):
+ w = test(i + '/lib/' + dirname)
+ if w: return w
+
+ #waf-local
+ dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname)
+ w = test(dir)
+ if w: return w
+
+ #unpack
+ unpack_wafdir(dir, src)
+ return dir
+
+wafdir = find_lib()
+sys.path.insert(0, wafdir)
if __name__ == '__main__':
- main()
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir)
+
+#==>
+#BZh91AY&SY�qK�l�����������������Ű„ 0���(bK����&#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0}^�˫m�0� ��.�R�����w2��c[#0���uW�ܾ�����g���ʧm�٩��ݹWMU��}��ۂd�;��2#0�U���l�l4 Td:�������Q�7�o'Z�<۞���m�XU����ݾ}���֯���v�����c6�W#0#0#0�#0#0z�#0#3(=}�s�#0}����E7L4�mP탉���У�:�q�w�hݚ%44)��(CB���%)UR�#0 v�IE*��(���:��B���޾����xGן>�}�=�W���^�r�m�jٷ�"]��g^��#�uv���&�ן3]�WJ�K���q�����2z�ۮ�˽�z���ӑ;ϼ��s����5�d�o�w�m���#Wݰ�f��#WB���#W��ftж�G���=�r�2�tѮ�24ã#WQ*�^���h#0���@��/L:h�:�#0���v�����v��P�l�o�ﶝ��񯽃���tO_n��#3�klӶ��A����V��p��|{��/;�����xw�����.����5�ޗV���g@!'�����r�@u̶� �����zMm+�;�v�s��[u����x�ۺ��v�ս{׻���0_7��w+�#W��nk��{q��{;��ﶸG.ض�������[;Wq�۽Ϛ�Oi��D�>�{���v�޽�n�kg�}��z�VK�ڻ޵�<�/v�I��4v�Ǽ9۸#0;]5盧Osw�ӽ��<�*U(#W#W���R��+F��m���*�TS�ko�=���2 #;4�o4zҺ���I+�����2�O{׻��΄�o[��x#0#0��W\�#0%k�۵��}����\�r��v���\����{��c��S����i�l{7[]��Y���۳���8���Qv���o|Ϗk����|��>#0�N���on��*�L�4l��w�þ7�1o"��n{�Mͫ��C����6��>��u�Ở]�ܦ�ێg�&��-�4�g�ئ���}d�o�۰s��j��Nx��.����;�J��`#0�zScCsm�.�놞��G]7z�{�8T��n{�=�[}�F��s��W%Kk�l7������f�K`t#0��([�!]w\�y��3��p;���!��h�^�O�l���:ݏ��v��gҺ(#3�q�^s����w��.�#3#3#0#3��-�{n�#���w�I��G F���wf��:r�ZTT�Q�#3t���ӻn�v�K�u15�{����I�9��F��t��r�@��Y|�j���D-bi&�{z��f�7��7�-�R��s�M�9�n̯mmw{p��t��x�����e㛆� L� #0��#0�M21M�4�4ji�OԞ��I����3M� MA4hL���G����=F&F���#0#0#0#0#0#0#0H$B@�є�=�*?�ѓf%=�?E4I���������#0#04#0#0OT���#F�#Wy#H�#��ɣ# �&h4ah#0#0h#0#0I@ #04#0�&�J��x�6�#0#0#0#0#0$�Dh4#W���C�4�?Tz����=�G���#0#0�?�����]���]�Z��v�<��(��ծ�2"�$J���*y+Q!�G�������?��n뙵� i4�j���wR<\�*�.�j��.3��_#�I�U�@�^��!�A�hh�#0ַ�6�j6M/������*�OZ1MDNG���Ӫ|97TC�*��qT� %J���z3�f����D?D�i�Ki���ukKj+i*�&ڳ5��kV/Kmr���@PiA" �Aj2tr��$PSB��#0�P$r"*"�b#W� {`�ʊ�*��H,�ȸ*�*����¶�Z����Q���a�%�#W$��M�L�̨MS,dcJQ��I,$&h ���2��lj -�4�M,�FM����FD�R��XԱ�LX��*P*L��E&�hڈ�,�Ҙ��TD,����e&i�b���V�A��BT�ƒ�0Ѧi����+��a-�ճV� RT3bM�&Bh� �m�Vi� �(�IS[j[fl�I��RF��, ٫04RJX�RȢ�)���&TY�V)0�*$��I�#4��)�Q�B�De(��2#$��0��A 3BQ"�C)�4���2 h#dQ�k%cF�0���e!#cYJHLQ%&F#WQ!�B�f$&�"Q�$����F,I��M�"��lm�� Lf�f�f��ړ`l�!I)6+Z(����"h�E1D��lD�0�IHF4&RJeb̨���jH�b iJ�$ �d� $�,�)d�&̤F�"M���6D�2��2B4���EHl�i�,b#R�Y#`�RD�Y$$P"$Zi�ƋP��̦�2��fiD`�"�X�m���HiA��Œ�Ȓb,LŠQF��f����6�e �E&cI#F�DH�a1A$ʙ��*��%3!�bf�Je4T���1I$�$Q�ђ�ȴ�6"�L�̦)def�"V"0D�I����L�R�#Hjh��F�J�Ʌ11!�2FL����c%b1�e$1lm�I&�4�e5&��$P�6�c�f�h4h��4�i2�"ƙ�ّ$��L�(&l� k6V-a,DĚe�$�mƠ�"�l)@�cb� �2�IbѦL�J`�+)�0RkI����a#Wd���"�*4�SJ��ș�F��Ԫ�2�J�,e3�QI)����-�K4Y�M1�%�T�d؂-L���b���2Xi M���Ȅ�� �1��(�F� �h�j-d�K)��T4kD����T�Q����e�FH�� Rc���Slʱ��B�ڊBѓL"Xʤ�22�B��#W��L�2�m�ʱ�)*l�Y,��5��aM�I��Ki�cF֋Dkd�5�J�e6�kE6�R�cf5�H�Hڋ�X�IR&#Wj#h��UB�Em*"��-��J-��**J5�h��*�h�h�e�k �M%#3��#Wa%�m*&&B�$ĭ5��XѴĶ5I`�m$kT��6iU ��f���$���MB(�F�����6�e�T�i�j�m�Rș��HŚh�[C ��2�cBi���ɶVT��Z%�c �0�QbCLƃ��`a`�b$H��XLb-�j�� QC%���icdɢ��e�E6R����D�h�̒’�1�`bjZ6̐���6�%�dJB3L��B#2`,(Q�M���A��� ƃ% �X�iL�b̳J�LbPY1�a)d�d��Z2b�L�Q�)$� ���Qe+#L6�����#�-,$Q���H@h���M&̦̤��YJJ�0hh�F�Ԓ�U5�4Ea��4�#31�Ƴ(ڍFB2� �bJ�i2�)�*H))l�ʍ��eM��Re ��4�"%#3(ɴ�-�["(„S3 e,� #3�1DC&��#b�5�M��Dؘ��! �h�l%EF��j�e��a�P�R4�dlb�Al �+,�,�B�,Z3 %6D�$A����ƨ�h�F�)Ti(6("2Fɪ-� D0��T�#3Li�T�Q�#Ji����d�2KD��Qc#)j������`��X�)�R�#WŔ� $��(���V 1S5%����-FJŭ��%f�Hʹm@�%2�Ԗ$��IFɢH�IT���I�b�d#35R�T�SmIY-�!M$�h"�M��m�Ib�Ɠ0F�X#WI�Dh�Yh�mbKmh�Q��R ���F���"�$�F`Li�b53d�����j���dք�Ƥ(��H�b��imE�h֣b�I�3e�[+mE�Q�M���E1�(22�S%cE�$Ѣ�M&m�53bH� I[MJi���c4��&4Y,mm���H��MLIF1P��Q�M$�21�U��m�LմcZ�ISC-l��mMM��i�FQDE��Y%#3f��m�͛*"��Q�L���Dd#W���el��������o鿞=����+��?�����MN&Z�$�8$�?�.b�V#3M�D���cx����*a&�*�����|qO��ʐчnJw�~q�F��J�ã�#WX.�UPUU$�TM�6����U#��!����?�$�+�Lde�K ���G�V-���M�C���k���-,���rf[f[��W'$3mIH��N(m��c�zݶ�.#0�?�H׿ �jj$v��D���Mtg�\�rh�w��jE��]00aZ#m�#+��\39��6��b�������is,#WJB���"F)&�m��{�3�`Wd@�L9j4��^��K�(�y��z^������VX� n��0Q`�wkz��[ -�J'T���A) ����C����j&]�E0�6�"���%c�ܒ)�+�X�D�T��$���n\P?�4{4Q�}0� ��̟��׷G+��*+9P��M�xcX� ֭VA�#3��,�Ɏ\�.�\ۥ��Uͺy�6*'�u�Ko�l[�wWͿa^��E3_"��sx����W�\�/�ϫ]u�����d���1�dCm����m{���UsI�gu�<s��mN#W�#WCLՉhS4B�,ZQ��]�v.Qҹs%��G9����^^v�v�U�_�|E��EH� C��v�˦N#-X,���OJ#3?{S��?7�$2�`�_�T�6d� U������1�0� O��.e���#W�[�_L@�:���m���%#3�MYl-֨�T���N�GMzm���A�L�Z����p.�������(�c������5��׹�08 �K�^ˮ �3�m���W�[WņqB�A�U��_���rvn��ɯ��Lci���d���:%�n�o%��q�nY0������P�ƅ�4<t,��S aiѥ@���~�T�R.��,��Je'ً�8�1��2I��B��&�.��"w^���+�o&-�t����3$PN4Q�E ��ʂ�7�m�Z�)u94�u� {��=t�����@��37������º�ST (����Q�r�U����o$X=�2B�#����:f��{�siB��ͯJ6D�1$������w������RtN�PL �r��RE�R��JM�E��6��� iI-��{������^�����r�Mq-=�V�T-�ȤD@�_�^1h4���4X���������o���qdDh#��"Tc�U�>��Q�Ҷ�f~ &A�}�O��ݘM�k��I~�_C��G0��,/#3A#�� I#3�H�AI˦:����uo�M_�������1�QL��UiH�f�k#:����VS��[4�)"�N)�#3D-���D�Q�g:�����4xh�-��ռ��^* ��hE��+^7%�ZZ)uI���ˆ�%%HzE#0g�F���;�@�v�m�T�I�P�Vk�{�%�*�㌓7 ��{���X��V�n�8@���QRA�t_�5]��{D#3�ц�y����C���%&aFeS?C�֯˾��Q�;���@��xh�� �}(���3�w��?G�Ui���1����T��t�>���_��*�C�%e+��oᒁ��JCV��ź|l-��z�L$�crFI��g{�-�HŸ��~)��2���e2U��.ɑ� �=Z���Jw�Wu�ŇW�1�#3�Nmk(S�?g[��y:�������\`~�2ף�5 �+���T-N���1'����.I�C�?<Y V�#0pBx��N�~)�f0Y�C{��}��*�6[�� "�e �?�1��Lѓ���hԘ�t��҇�\_�Mz}�M�uXa�H��Č� ���Z=�{�e�r~eH������ �h+��q�ơUa�̤gN���P�(Ib�FG���}����ߴ����ϗlk�PBC��Dnu����%�q��������DE:�!`��%�s�ջ�8:���� In�% ��i��\NE���4�V�N�Ӣ�#3�P���l#���j��ا�.�A��ds�2��h��)V<u�׶ɣ�sk�����w�M��v�c��1��毈u�[�p�֫��*#3��4������_n�F6��է_���G���wO����������x섒H�eT���,O�u)�F:�� ���rh���5}��8��/ō��"~|}(�gDž>���L� �?A b���>��7��=�=����6��]20ӛ��#38@��V��[��E�#W��h<8f�K���M ��U>i��7��s5~T�k�݋��l��E�?D���ʻ}���W���#01KP�a͐�(Ti$�}���~'D��w!�]z����߇���!+��E�<,�f��fYB�R�L_?��,����I���U���?�u->����ͧ�UE~ >�)�ݷ�u�"�,F�������b;�{:��_F[U#3l�QW(Ā�w(s�xK=ؠ�ٻ$O�5���:n�%6�\0s��PC��55�)����x����K����mk4��O�-n�#33?R��������.�P`/:���IvS<f}����kn'�G,e���)W����rE���aH-���7?vf��ʡ�U h#�zD�Gw|C�ۭ��:��郪`q��U�i��{�q�QQx���(QM���a���!�����S��4c?Ω�����D"���Pe�|X1c���}�~�1���{���F��x�ܶ��0�(��4�n&����(�9#3װ�����#3W�ZX�(8�du��,t�#WC^A���1���(�EbU22G�t�N��C��b�Ha�5zO�&�����K���lt���٪��i�����J� ���V�]��5�fb"��)� #3����h���)�@�#W:�~��R�T�re)4���c�q��Q�u%���ª$y��#3t�dq�j+6������kG�{�f�9��t�p/�ѺR�tB�b5@R-��˕��q����Dž@SA�R%��#W\����:2�1&#3��篖��P�6ܝL�a�!"�I8�����2�9�&�_�?�[ٟ�и�v[���+*h��J���#W{ ��5�VT|�?�q���9<��������"�-)p��"Ė����O�mؽ;�wy��o�4f�ak.-<�4:L��eE���N,��y��}��%���_a�5 ��fG#�m��C5��|��,"���j��"�n���Y��k��0�Ca`�Ŋ�%Z�w�DH�r(ԼF|)�<���u|�J��9A`"*��U��75�Io-�f�VJG/�#3/�r��`#W,U.γ)\�d���>��Z�Cy|�� ���o6�F�p��}�~���Л+枟��2@Z�n9wiL�����j��|�3,eFÍ��/=�=h{�+yq�-�~[����aP�(�<�h׺%����j�����Z�^޹pc|�vX�ד�/�/- ��[�T�i70��=G!�Ё���J;,y��Ǥk�����b�'m��w Â�S�C�Pb�����)?������V8�Y������~� �s־.�����e�Z�I3uwRl��?�;o�.���1�wXM�v|gX�lEp��Z,����.O�u]*����}���|�oet��ϧbb��ʺ���n?m=����m�5P��#Wst� ^�x��:=P�t��B�^��x#�ڌ}E#3qU�q�kWYK#W��ΰ|;kT!�_ә% ���G ������.��LٗZЫNأ=�KH��~)�uv-6��d��E�oYi~~�{f����ΰ�D_==��t����b�ӆ���:=�D�I#�a���H��L�k�K�5�F�J�kL{�Q�բ��ۚO-�z�>��T��x�ڀ�j{�,��b�v��6���V�EA�����3Iu��l뻍pLk�20W�7/o���<�(�f�Tb�,�L�؇wdq3����$��r�փ*�}��3�� Q��iS�Ǿ�f���[>�.��8�nj�/�_�E��gd$����iPRl$��T-�V�'�;)��/�x�_h�!ѣ�jQ�^��##W��c�?��SS�p!��Q�I�ox�$`{#3*sH����sMҩ�-���P��+�E �ر?*_co�ړz��뻌a�}�Q���ڤ��.x��Llck���_f�x�Ƴ��ǯ��OɁX6�Ѡo.p���:�Nҿ���g4��s����#��4���!�%`�)z�ɰ[�����G��#0|���>�+�M9����J��Hꮬ�sڎU�;vۿ]�:<A)�im�T��P"1b���&0b��[F�m�7��Q}L�d��z>Ԩ?�]��:vf�6u�����'0��D�O24�p6�3|�Tl\��g�J�=|�an���v�r����j8څ�<�������\���]e�2QI����ĸ���1���E�������.v#3%D�y��b��Xh��#W��P�#3ܞ���y=�d?hJ?͝�F5�C���ż%8�%�q�.�K#?|Bt������H��6�f�YX�lG�g �Ff��J�P�F^>�������R�����7���R•�����E�V(�}���aB�%�]XD�!���M�W��V� �m��ē�t�R�G�/}^�o�V���;��̀1e��#�����Q��~���&�����ˁ�`��v��@��U�������7gr���Q��i[<r-�� w��+�;��?q��큢�$�$џr]�E���T�ouDu�Ll�6�'dЃ��ꄆk0yìf#3r��ldiɛ��Vr�����6P��g��T��"����J���^X�ϲ��(�ޱ��&D� �dX(��,(�8�$<����-�9���x��`����¨��܎�j���Kª�a���~lYF�6���6#W�P��/���2�][`�ߚq�Բ3̨�6�./b�b�*s�泙��5�n%��d^E~�.Z>�<S�,�s20y��Y֪k1���\F���^ ���w��I �w���t�nq�p��o]*�gJ�����q�x�% T�R�K�5��âh�6����y�$QVRh���� B�q��{U�8r<7� ->?Ѽ�X��o�H�ix�#ãI�#3������#3����9igkx�3\�4V��"R?X^�)W7��RF���}�6a�XCV��!J,��ؔ��5y{�j�`�x�>ʊiS������=��Xa��+�n0X�vo#0#3 �r�NPv�j�0��s�k+�'���#b���D����]3�_��qhu�9.Tęe��.u>DC� aF�T��lR�.�m��\�h&�`����Й2K%��Pa+F�8��`���a���SH�#���TI@�3�ӥ9�ğ ��k�}��ɭ�M�ѕ��z�օ��:<0�/otj�h��*��AD���DL�u��C�p�T,U�j�uT�B��'��D�y�i<ӤǢݽ��_ �MH3�DO���J MF�ݴ���Rwnϣ'(�h��c@�2��p4�B�f�k5f٥�]�}��Ra�Q �ԋ�l�^��٫���r��t0���1D���K��>���:$��P�ʑ.$&���֡!���v�S�Ɂ*�g��3���.�7�[��kw ҁ���“J&�����P�@T��PX ���|�n䛪X�nY�j D�T:*�׊Ȗu��A�� ��x�T���0u��9��W*J0G(��l�s�2�_n*���`�z�}�#W�v-�J#�kʣ?~�Gm_Tbv��9�t{�E���El�%����_���Y�.��F�%&6�86���͜.�^M3�o�[��CG=���3#u(��{D-U����i���0#W!1A��� ܬ�O|��Mc8h:S:�g�J��z��P8�+g���#oh�Q���ܽܖ�+4�&/�ZM���':��f�J�l�#3�Ŗ�+�(E(�h �S��UW�:�;�3�!޵�#3�:��� ��f��m���Uk(q�)�0� ���[����-k��(���2���C�P����~=�Tk ���>mo82<?���Xom�w����o�����L�TE�G��_9����n���Dt�ؼE6x-I<P���#3f�6����Q��{?7��GG=���PM��2�n�� 0N/�B�������j@��չ�A�+��M&`ya�i��*0��I�|*ǧ+�z��c-���x��=F�5�"�s��9�|��h���jDz�#���B���]�)N��t�^j�� ڪJ(�!#W+M����5�Y֠���q�`�D$�~���Z������3����>��/#W�[D�ѹ��T�3�1�Z��ӧ�\�[ao뽶��#3�� G�[�>����S@��j��ES���3d��(�D#03�oW�psEIzGN�#�؝ ������ݯx�7�~�G/v��3��vY� g����G�#3Z_��%aa͢���N�68Q4`X�D.j�tW6"~�������ס�P�czhl8GRPS�H�^�-�����d���}:���G�#W��;� �*�G�����ޚB��T�c��x�g��:渿A�0-w�0#��A����V�!�fj#3v��>_��e�������`>��q<�D.?A���+\-�$\|?C�#0� ��F�'��-]v��f�j��S��v�o{|6����xDž��U�<֚��Y��Qդ�������W蜑��U?�����r Ϙ�Lݪ����6�l��ua�����߿Рk�=�D�1H�?�1�I []�`�\rtE�g�^�w�\S@LRJ�J�c����sZ�#3Ȓ���?��Oǁk7J"����v[�2��Bt�*��B����sG�#0�Q�L�S� Ȉ��Г��3�d���������� ;{zߟ^zlp���4ĈúY�P���L���9 ��R�����ߞ�9u+�����9�iG�UK�%�HL��wv�?��3�kyEW�Y�ԛ����� ��w�l��Ao�k������3E��v��"#0(�"$��[>�^�Ͽ��R���hZq��G2�_��e�s�V�V�Jf�^5P����s��1��<l�o��M���^G��`�,���Ļ��7-#W!�#Wrf����O�ۏ�[S0#WВ^�!@�26��J4 ��������Y�y{ݢR��s�va��%�w�>�H��c�2Am�׬U1��N�͎6�� ��xו����$�)������&��#W/�~��v�`ta�zĠ�s�G6�X�-%N|襤`��MC<�Ozm&ń`�U�������!�oD>7ԝ3�w���|��R2�h�%��!b��+�8�T�X*��q����@RIl�5�>k���*�69��܎�"���%�5‘�u�J���!�rܛb~�s�-0(RF����_���K̼uW��tx4�.��\ߑ��ϓ'޾\��V�%�#0�>���쫍U+?��]w�[ɳ��O��u��Z�QPC6^}qd�@��0m��][W��RzN�a�ŝ��]������ƄN��;Ǚ�lSvL�Yj�L��Xi&��C�s���p{��ճ��k�;"H���徭�X�Q��ou�m<W�8��m9�k͞�#3��p��7���K��.��ټ���BӮ~+z��������u�* �k|��B��t�A��� � K{���qe�a��[��M&N���-�,�����$��Uq.h�0b�o����x!����� l���3��tჟ&^�4�T�����e��B}4�OI��L� �5z �v�}�%�j��7�� ��&Hj]:fL{��o�j��V���x�F���:�Y+B�;��K�:r���u�y�/��;s�ސ�2!�P�ȉR�h�6��H��h��x�ζ�k!�F=*cx8�c�#m+��"S:iG�5姘�j�m���Θ+��d==xp�z�E^R�QDX(1J��Ռ��!�T.yS@t�7Z/����&��� ���C }���#0����hC���'���WS@�|�D�(�_(n�iX��3��hײ���#3�/@�!#W h$�B@>���#0�6NO�:4�Dqm�Z�\v��J)I�]���D���m�P>~ձZI)ne�K3��W�ڨ�!gZ�|:��*�s�:�@��,�#0�W�10������ܩ���������������MT���8y�n�}��=#�����tgG�Yj�ͅ�������#0��-xTXPy� k2�QAB3{��{&|��N��.Y&h�O��Dž:^T�ߎ��\�厉��۰y�K�!�:����c�|9h�����q���e�S�+�s�ؽ�4 Q�|���n�O0#8����#3��ߺu��3.�v�:����e��se�P�<���0�s�rԻr�f��Íc�c^���K:�K��p��$qJ�.ã�n�Ǚ������G8�t�K\v|3q����z`;��K�`���u����ɷՔY<�Tz�5b���&V��R��pL��=�ա�#0~?�^�)e3�d�Q����L��Ba]3�S��S��m��EΝ�{m��fp�1�<�#W�r]�#Wq��B�b�o(k�b���� =���l�T"(b^����v�����h��"��b�(�h��4�0Q%[t��4g��.]�y���&�R4�5"Bli�#WI��1\)�a��)��M}�|pX��7S#1�� @6#0�C�#3��)}����N1.`cm��F�wH7�ᰑ^�ұdOiE��" �S�;���<�2�~� nU��\�h�J &��4��.��L;;�}���I8�'#3����C�)K.���最�g`�L�A�p��L�]PS�#3�������[hx�cNΚ��-�� wHX��X�t<������FX�D2[A���D|�<1�ҍ�oj��[G�~x����v@���tC�@�t�k}�C#WE�$��쁀�o�zU��g'Y��@?-��g�*�cPD1_��f�3*5F�mL X� T��\S�:.uY�8c�p�T7jb�C�d��J�`�b[u�S]�-��1����kL�F�1 0��?�#W���H�Z\�b1� 3e �.�R���c@�=��ĽG�Z��R����f��-�P�#0� ����7�گ�^��U�"��#X&��m�׻om&Ѧ�ȗ 1��l��+&n��&+f� @����HaR �D$#0�0����S���n�Lfw #On-�y���Q�� ��}��Y�����}�~{:��>C��YY2��)s���@Ao�|#0��T��rj(-�9`��1dL9�Z:�U2&��^�#W~Y�z��e`s�z�T�/t�6�8 %e��ͤ~o����Չ:�~vJ��/�#W<���PS�E�F׳GK눯aO2uHL����� B̸.9Q-\�:�����m�v�;�w�7��_N���Oʥ�kD�ș��\S2��ieM ��Yk71/��£��8���J�u�9O���-��Z��H�?�s4�9�x�y>���~R��#W��Xt(�/]#W8�I)!�>Kt��A����ɿ{�� l�x�ɥ��0c���W�N��������Ø����glqޝ������ j����L��t�������km�OK����[� n}�+L���$֩(���(00�Z�xN$m�%۱��?�v]0a[��Lh�>90�}Bp�Pi��RH�n?fQ�0D O��?��=���\R��?�_� ���i�s���$���+�LP��{�E�m� R4!K�~�#3��}�i�/:'Di�����#3PS��F�9��U�DVA��])���谫 %$�(6�JdD�:�a#3$�:��أ�P�Q��E񴁵��0���] ���M�V�#Yk���) �P�]�J"�ECF��� f�G2h�bd+"Zq�����7P�S*�PÔ�̪T�@H$���$���+��]����<ғ{�#*)��=$����7�.t�� ��ϞO>x��SM�=����>�9ҝ�����X�$�7U�|<T�������9�D���j�1��{���9�R ��=�Ѝ&-h����dG��7�h��W�EP���b#�1�ˬ���;a�bX-��GK����5���\��1�n&ݱh�M�F=�Dyu�o��X7(Ir$��\�5�.|��x�� xs����s ̨̨P�f9x��R������i��{TU�2���E��"S]�|���56�}�Q��K�O��NE� ��J��fx����r⏘�������j�w;����F��F�N��C����~gWgE��ۏö�2c� 4�$���;>)͙]���,.%�?ۜ)ǐ~��jA�k#e��¯�H�N��DTc��)^.�3��מ)�:*P,ܑ֬��N�5��}�73�{��\�8�T�I��p��K���^[{e�f?o�4�Jt�"�"dhg���ۗ&U���+5P�1z��v�\��a7�ɴe"���r���5ݡ�H�ӎ6�+�T(�ϡ���=أ��*�z��6�������&�g/D)���:����^�A�X��u�wn���)�{1}��$T&ѠO[�� �����y+�;����sYf;&�lB#0�����C^�Ko���#3��h�ɶ�7vƭt؍\�$֊��n��IH'��"�D# u�+��C͂�!-�bop�H�M(d����Ѯ� �Յ��O�[;��y������i8���#W��{�pOF�A>�D�Yͷ �2�����!ʵ'�>������T"��*-<�O5MS�o��K��X�c���2�f(p�=�X�3 �����^[�#3�����_�~�=i����#W���9��#��Z��+ِG��x��#��x���&����]�(?����V�b��?��N��q8`����'��3�>۸�B}1uw�ɾ�p��#W,| �3�X���br'�ɭɌ�7GN���P��)�-�������z��F~��E�꘽%�l/��/W�rS�~�������VF)�?`�.=��:�Tŏ'���9����>�#0ZoT�p�}:sz&����r�� 2���'�3�~__��(o��*ߐ�-��C�ѯh�p<��w�#UJ��b���W���H<��9���9�"N�.�^��=We<k����"&����!�a>���d�e�Ɗܶ�Q�j���}� G�8a��!$����S��FA�a]�2(]&8|;�=}�}�|U|Kۓ2:I�ڡ �@�K�f=���O��@��(��@=�Ok,<?#3������>�K9�#0Al���U��R��|Zn��c�`G�>b�͇�<s���T���> �fLO�LT�)PRE �;l�]nLڒɢf 3eKE����M�����۵-��y㮮��^ ��+(��@�X��������%dR��e���|��E2USIN�0����n��k���y�y��j� \���J�i����m�^�ק��q���?S�T���B���Q�EX �)����v��u���Q�y��~��H�!V�����_s�B^����&��&ue �`RsC_���^�6���W�zIQ@�B�ʢ��(y��a?��p~�1��ZW!�[�R4��ɍ���&�>�Z��B�,0ʨT�^�m/�1�i� �������7+�U�5��w�Il��>��/���O�jŋ���K�uc �B�Y/�=Z������;�K�4+�7ڨ:D|E�%��̾3Q�\�w���D�~s���G6Qn�]��#3����v���CCe=ԇ�2�����e�6��E&>�?�KQ#W��u�,5m�}n�a���|J"HD�?�-�M�b/PF�a2N�Ab�KP�u�ڿ򆩜�s�_Ҥ3cI��$����iG�#W#���?{I8&�V�~�Q �(�d��L�a��V��=,��>do�=\}v�����y}�����@G��%��|E~�u��d|�����-�o�W8�*_�ŶC����u�����z_�.�7�4����K5����+�^���B�-l#�?�r�>�{k`[_6����sHi��b�97�H��r.���5�9�Z�T9L��|9�.{~Q��gO #3ib4��/�O����Q�˼�r�s���wG���RV������Mv�rSC;���8Li�qU _'�th�m�!�(#W����;�Ou�^� �ӆ���m�cQ�^2��4�eA@r��^� C=��e*�H��&���n��;�D��껅j���v�K��W�`�G�5�eh:'��:��Q�!U�p�V1�@zp�D]�ʱ)aI����G�;G�/��!���/�gۭ�w�����h�RO�#3<Tr� 0�ú��O@���(4V�*�#0(VR��r�u�0~�=)�a�L�]G���Ӵ���d����L]Ka�йĨ�n�#0T(=�#0�cG��c/{9 ��H28�p�5ii�U���>�uОWK�4�䔳4ѽ��ful������50m���Ok�o�ݶ��b�[lz�������/Ir,�A�ɘ�VB��$�1����Q~���������\��&Pe�! j�"�P=������L���y�P3��/f6N���Ĵ<0K;}�{O�~ � ~��Q�ԓДLYVg��:9MYi�#W�� �i���ߩ��Ą��������?W�c@�s�j�� ��� �f`eJ�́�bW����\������4�"?ç���{����[�7+�;�<��.��Sζ�ijd�Bt#3/�<7=��|{z9�����ã���ti�#3`���t��.d��*�Q�W��o/ȿ&��&ޕ�!���K}��3L�N����j@�.�D�C������ճ�}��W����O���n?!�g7\P{}<�!��:z����+߿�G�_����cz�u�?�����L�e5�M}N�2,������I�7l�b��f~�_g_�6&����o�B�燓 ���j����o����\<'�ڶ������e;hv819m���v��Wέ*��"���~�����>]}���r�<ůFB����_���S�,K@�w�S��U%�W?�K�y�=_]�/Ͱe~q�W\�{S��2��̎B�I��~�|��K�v��><ӕ[�E���>�'��/����R��1�1���a���u��}������������5�>�GvW=^�M�P��#0�0����������Xy����t�mVF0�Fѵ�MG���6���4�z��>d-�-�qG:^���+�Uu��Z��w�.�6�oۧ�k���������]��X=����Y*���pa�F������Y�>�h�a��f�"s����������z�����q��>���>�^8m��Ö��ރ�a6�#3��/�׺4����u� #��e�]���,x�S��̭!������ʵ?�K�(�C��#0sw�\R��Ȣ���盷z��.��r�,���>���o#3ce*����P����^�����x���Xq�e����a��6�j��ë�l��#϶c�w��j��������M��ӭ�_�[_���h��/�J������d���0?w$����Ű޺�s}#1��8( � 4�ٞ��S���W�K|9'.-�B9��m������=���Q��TL�AM��iס~σ~���������p8O�?G���5Jy��:��:p�>W�� 7�6�z��]����$��2���.�/_(������ �#����w�:,@����}6~�b5�e�k��h��pw��dmٔr�;9���=Dkh�W������(�<����ѫ��CCO�e�:����磒�fx��Q�����X{��һm��a��������~'���[�=�6�H�`�N���wҕ�� Q��a����������eᗑ�iu����S�����TjT  b�Z#W#&*����>x�� �����iٜ��*m7�\����T���F_�,t�HG���h��~?8���j�@#W@Qiz,�4��3���X�)W��z���g��O������×A���g��/y���6����~^j�9�����˲�N�s�աuk�鿗�hB�Ή��_�>N�����:��p�������t���#3�;�O���=���w#3�K<���y�<U��V�伃�o��X�4:����8���_����k�_}���-�l�Ch��N�:���{�n��,���ip؜;����u�U�����Nkc���[ �C^�������y������������?�?�B�*�*P'��������{�I�����<����Uo|���O���#W~s��9`~��*�U�b�߳ ��D�H�m#3Ȝ8����!__�ۗj��ܼ��2<ߝg�S��+��d���E����V�TL��=�Y?#3H����_��Vf ��'�Ug���u�N�6�=�?7"KL7Hj`���#W��Ug��+ B�*��T�_Z�٣�K`� ��j$k��V���#W<���%v��$��H�+�U����>i�y�ߞ�K \>Hq�m/vYN��r�u�x�1֤Dt������G�۾��LxM�Sj�u�Z=5�I,t��6À�D�!�l�{���G�g�&��zw`���8���͏�p�-��U��E:��{���歹҈�����\�puϬP��>�+#R�(f�#SLT61�z�����g��w-._)�clb^jP�ӛ�:�J�9N��#3����P�fp"�'�yB���i���L���-��K�:7���($#0;,���m�����-�*Y�9J�˂�R� -lCĩ!�@�h��+&3J��U��p��œ4{�>'V+���a{����aA$����� Z_��SQ��}Ґ6�ck��T�������5,f���@@h�=��tWw���g�}�O�G$����� ~�D��!�ݩk��owP±�A���ڑ�#W�TD&�L��s�MZagJU�}������#0��k�Ϛ��-?���_o�~=������ڪK?����A�^�N���}$���K�)�a���D���=�G����c��#3�� ��Y���#3��dژyOx�k]n3y_����C�Q��3۞�k�����LRIɭ�ɂ=�el�Ua=q��J�eC BK*��O�0/_F�ݿ�P���w�f�x#0a�Ѓ}�L y��P�a�a�=#W��2Q'˘dG��n`A�U,i/��5���q#0ǖZ�i�E��*7;��<����@Q�2��Q��2#0̃3.4�6�ۑ�Ya �n0Q�aRy�`�����m�R8�9���R�4��#3d##�<J�҃����-������ڞEVj��5fY���" %�K� [�:j�9�1��L���V(��#WKU��Պ�# �"�MR#@���ɦW� �Y�d���I��i]꺻n�B\�l�#n�Е�"-��d�u�Ɵ��2"�Ì�6�w_��ѰѢמ&\� ���������0���k��D�B����B4��F�d���ͧ��oӗ����=;M�#3�Os�PbG#3�f�::��f/��p���]��vI���o<��a;.'`�����U@qPJ�L۩�_f��Tm�f��8ڵX�����X�݇d�M��7���wC���g�F���,�,`*��U�MCM�����*i�t{�|���߳g����� Uz|Xg_��z#ڧ/���ѻ��' �$�~?��ٕE���� �|t�+lw�~o�V��%�ʏ�EӢ���ͪ�/��9������͋�����E���X]��1O���)� �$���xK��>�������~�?.�>��bA�+�s�p ��T����,�����wX�/�V���ݚ��,uz��2 �'o�ߋ���s=����#����Tئ_ǯ���j�3��m����5U�M�"m���V��� $����/�-ݾ@��{�ڱ��.6�-yA�魵���L�ѭ5�b�7S X�REn1?�f�1�S�K�vēZP�`� �x����<�U��y��5�M�&6#3�dS��(cm$v`�XU�5 1�TQ�􍘣�X�A�6$E�vB�428Ү@��e���Z0Dp{RV޵�hU�x�/-4V����#W�H pm���d�y8�.vf�k�!*RH��4+�8D���4�0 �"��m�iv�.��pc�$�1��L*&)ũ�v�-!�i���!�I��-�kD��8o�) �#3��i���3�K@0ǝF�:of�(���%�L�����q�c҉�5��(D�tY�@���GUh"�<�GA��W�eF7���H�!!��:]Y�3�y*�ĐRJ�sV�"U���++`��f��a�A��N� ����o�&(i����|ԟ��{rs�7���Vln>#3�#WZ���?j���N��ݶ_E'�Nu�ۗѶ��G��vU�6���O0$K5쒡�y����~�[=��\�ʿ�z�tk�#W��NN��A5�K&㘢�"699��S�����x��eH��}��%�<dl���9<+�V��a����� ��\u�ǣ��k���Xƍ~ؼ8;K�mo_޸�F�#W�h�T]���9��#��\���)�A��� ���ڗ�}{��G>�Y��${�6��� �&�+�aE����y��w(=,Ē\�Us9s�æ)?�A��_0�H��B��3�D‰x�A���U!�����l��H>��m�G#3#W�U�wU��A�fg��m�#�a$�#W�O���E_ QFG#32`�v�#\��#!H0kA>��snFy�:���O���;�.G��&9�b�s�I0i �~��ӯWP�.�^Ï$�j顂8vO(��"�����r��IE(��,GP��(�+�*إ��e��HG�"�5��+`����D�: **���TVVUDdZ��y���|�|�*��C����^ӷ����?^On�����b�@D�������\�2��`Ǻ�u� ��12L�K�&j���̬\ ����"���X� "Ԇ<sW1�ʊ��B�A��¼��X<���P(�A�`��0Y��_S��ͥ�^��M0�ö�7��y Ztuӿ�´b��A�`��7cUPߑ� �Ղ�����Y��]ۜ �شB�l���e��A�9�C�Ưy�#3a��u�p$Q�J� "�L���5 )*(�-���2ņ `�, ;c�`ѱh�]�]�S�nȢ�1�����l�l�� �2�f]��Sq�7��4.�gXoں�m��0��J*�N�ً��wR\�[{dU���yܭ��4.1��� #��B҃��~�G�~=��Ȼ�!B#W�oE�8�|\w��v��m�}�U��H��48�(B��L��"12���& y0�q`�AP�g~�nZ�s�6�Ѿ|M��'I<�6�K���O���Xi�ɟ͇:s�<����nW*���#0kX�#,��1b�Ҫ��J� `ӌ���Y�� h��G�.n0�pҌi���N�#W���,��G�#"D��0���m�V�v��[�;"V�r��.2�C��,<6�A�ԩ��C��;B+��z5����+oKD �'GU����ns���Uh�#W��=��?���lw��lX�oq!�- X��9˃_q �ᯇ��������'G������q��ν��I�b:?�|g�60E<���#W�3��]�� -+A���B�67e�c��ḛM���A6� �����'�#]����<j�V��rIJ��m��P�Sq�F⎎6���<�C��j#3��+�q��/GF7�%dF��Y�S3�\Ql��f�R�l�A�XP�E�F�)�@p �H�$�,B7�2�(�%�P� ��� �d1sd���MpKGi#3�7*���\ڵ���n �]d�"����)� �B���w�Lc9i�#3�'�q""x��#3-CN0�!�>ǜF��?|![^��dI���V���� `�C,�e���2�`^�x�{����u�>I*f�-� |�>�3����1�j9=��Q������*AD@��+L�l�*.H*)P 9��[Hך�ճ�ep�;�Ҥ�������C��C�p|�����K��ޱ�X�~����զ�3$^';���z���iZNA)�V(���n�l <�l�R/��#3qs4�u��Ͷ��OH���[�rnH�muKq�z��Z�[H��0d��%TBw}��-x@kb!�y�5�^-����֧�2��E;)�bQ@�67d؅2Q�m��R ���j�x�Tf���*�(H?a< c��a �ah�I6�.2�DFW��w�D[�m��s%�f�&���0��B��c�I�NV"s�~�I ^8�A"��Ь8q�J�}o�m�$o���/oJp$��O�'�3z�#3�p�3,\7���6p���X4�jmG��}��aCۜM:�౾S��ђn�wi;uɑ���ם�1�5��D��H�$� ���P�no��No��#����K,Ԓ@�}�_-��d|��|.�O#W8;i�W�6�܎l�M�z3�v,�j�O�����ܻ��k�ֻW*#��љ#3n4�B��|&KY�%,�xt�w��qF�Q�u��VM�%���7�Ӝlsyl�TZ�F������ַ���D{.�Z��:1M�WG�N{.^gĂ�ߠ���[�ƭ�cQd��Xe� ':օM�h��rL퐄J陭���0���:�2nr���9i�M�I�O3f`˾�q���LJA��Ka4I��a��y�[��6�@�%�FW��y�^n�&P�]TХ�C�ROy�#3�w�l��,l��z: ɗ�;"1�!ļ�.�;�MV��i+��q�'���&#3��\���?Se7���sq�������Mc��)�E�þ����qV�:���b#Wrk�[�n:�tf�����S ���^��-ee,��=�E�=m�h�0��2��os.����)p:q�I����S�e�����7�7E��mӣ�J�ϝI�L&L�5�P���1I������|L+�_|j ���E$Ŏ.�����S�i��im;'�y��= �圕�9�2�%$��M�3��!���M*KN�:���y��m-x�_��7� ���<{�k�o�@�9t�eG8M+Y�^0��ݰ�OK {t� 7���#W&aX�FSE�#W�åy ��͵^��n�N �nF�~�~it��3�uګO�틡4|lM�li��+U��,(�j��,׷��.�9��6i�) aLU(:���4qu�զ�J0����;��ɷ���?���R��o8���{��+h���%�c�Z��jm��5�SP��{�FLQ�~?��:���6�^쟃``#K�bE��W�n4S���Gg�xy��:�\�#P�����W�G8Zq(���J'YB�R���ȉ�q�ϽW��?�?��ϠC����N Jx|��Ht��?X�"B�*y�oC��#I]����-q�xy�ڠb�m�ц�o�� US��:�<v��@���0� �Ea����K�/����7#W�Q�']z&��O~�f^�[t�Z�5Wyn���U��x�gQ��XE����H?O�-����wJ���2�&�����{L��m4�=L�-���8m���~�������%��.�gm���Qn��B���;1%�<?���2��)zYn����3n�x���۞� �_��?w. 鿰�|��k��\�6Y���9m(3�=Ps�� cp���n�I��a������"H�����@Dd �K#0�<AE�5�L�� �-�@�#��F#W= ����=�G����q+[�L��Jl��M�`d*Jԋ�c*�7���lg\'&Wo���� Y��4�jZX�3M.�Յ��U>S��P�7�x"��q�%���ą(�`�a���F�`� ��R;F�̝�����u�''�r�(�C�֖�E�÷ngc����pvᥐ���7�14�#WN ���8��#0�lx��т! �$ڷS���Qj��[j�ݯ=d�!R�"��l�| �lp)��Z��a\���8,f���[���pߣ>ߧ��cF��j�.�0�i�FB�u��{�mꎻ��3�:�#W�54�FQbF�� f��6#W����������B������Vp�U���ɻ��;O�ҿ����#F2H9���2]ݭ����=SQ'#���w�^���I=u^�,أ�8��옎�á&a�94o�Jo]zN�?|57N�7r:j�z ���6B��FWV,x��.��xk��6���Y��<�s4���Ԕ��羽�����7�b顳�~^����xi?[K\�<�1z�f����"֥�L�Ӱ�1������Ɍ�A��퐆!�ۻ_R�d�#W�6:<������{��!�و���0}z�F(n �#3Yq��:$��T�e�nk�3��:�p�,�~[ii1#��PRi�ǚ��b��������xf�-0�%���W�x�t����-צ�E��r�%50����[���S9l�ip��/<Fd �o9R+��0�hԗ������[��s #�#3}��.q�L��Q�;x{.zk>�b�4t����ύ\�x�n�y�'�tBr�[m�N�O^o�����5慛?�ֵ'n�[��]Z��̻��<�~P�~�o|��q����B�tw�Kq��1�HF���<�jJ��w���V ��%@�+��#W�&a8������=�Ď�H�x���)k���k��s5b�q�����GwL\�語mA�"�Y^o"&⾙��#0�U��g�C��\>�O�_k������w�6����g^U�>}��$N���J�&�v�N�[���& j�#L��c���$>�˦���ڲ:U���.2+��(ת0�&���ѯ��(�ǻ�`trr�X�I@�A!��]tqG�ٍ:��B؇�U)�J�\�,��-�w�u֚���:��J|c��O�:�_D혗�����1E�v}SQ��2M�//{�� �a3��>V��k9��� :��L�W����rPU;,�WEjC��i�˼�>�=�{��z�7'.��&����#W�K�أ�i��Y'˿��a#0 �f2 �f�T�'&�2P�~A��K��#WY��x3�h!kɺ���n6cJ��4&�O��U�I�n��m��Ӝ��M|�lDٺ�E��z�y�v����.��qf2{�T_Dly�A6�}�߿= ��dC�L����ϟ�yծD�|���8���G��ߣ��xY{;��� �6)4jO�)�%�s6#c2m�}���3���."[km��u�}��������[�F�wg��o6���72a��'��)K�Y>{�;t['6�&17ZS�wfި��8�+(%��!�Q��E�.z�����1]�8�w�W��<j���#���-���#W�ܣ7��5�W�/[?�-T�ᱚ��\p��z7�Num�H�c�9#0�����Q<<���}�k�L�lm�ο�qi����#3c>�^q��F�r �C���ԴZ��Ř�r�����\�w��0l�|y*��sb)������H'=�m�P��K��s�;V��m;q[R�/z�3DA.�n�]��ȧ܋4�1�����������>��^������87#\,3r�NC{ﴜ(�6V#3R�Uk)(BZ��!Ke4x�y��7�dM,�}��P��'���E#W��c'�/~5�H�k�H,��� x �J��,��g�k|m*��ГzX�t�!� j�_ f��ic�� �P�-M@u�����p}������ϷR��_��`ΒZW���E����L愦�4M �\'�������|E��� �kT�mX��<�ޣ�A��D��,��;�.���= ����!"A���KS�4X,ģ�a8r=%܉�_��X� i��T��-s����Z�*A@\�/�M#W��k)�����9���M�!%a�R���N��[\�Pn��o�I��9��S�K&��Ж6�_#W���#W�ص�5�vb�Km��<������UkP�f����Ė����!ap��_�E�융����h+P�d�[GZ*�(�jNk���W���=a�sK��=@e?��U��a,|�ÓFq K,�nJ�`�_;_%�bx?%a|�}��q[c�����k`jD�5�I��'a��Ʒ�Uj�_H���,`�RWs����_����{��F�w���1���|�z��׊����l�|�8�9�τ��[����]K_$�#3��5w�׮��c,1�1R/���5~�7�?͟���Կj��������S`�����:�o+��ϛW��(���s�����Kyn|�{!?�� U�yUF���ݜev��UP�}�dJ�V�M% j��8�\�%z��������ng��v���&��x�#3T�X?���ʏ��ןu���꫎��{�D��J�/啯� �^���"���G7Px|�,���5_}�`���h&�l+f���\,��]vjĔXؾO��9q`�χ�Tp�A�7�8Mh���FVV^��ז6ⵑ�����]͆ �9��ls�}��9<*<MH��#3˧4�6#9i���NÝ�/T�ӡ�F2�'@�U��"��*�5 ��V�@ļ�a]l�۞����@�C%���,�I��Ǿx#c�����q+���5E-�ԅ]H<�W�#3!�e;%Λ\��Ň��u��,��w=o�S�p���<��u���{��c��؉�N"�c��;Y�8�c+��^]���jNt}��~#|m#3���T��U�grʺ�2.^���dcja�`a�"��X��Z�e�����h�lm� ��՗�Α&r��Xn�����:�;��Ih� N������)st��"̕_kx�[ǰ�0xޢo����v��%�c�z�wǻ�w@U�H�KdY���H,�#WC>��ϒ�G�=q7�>� �t��� p��yPq4��D8[*��@<�+b�)+�6KH�j�Һ�A�ȥ��#WF�Bˠ�a{�k�4*��m��j�7��K)�Zۨ�p��L!e=�9��휣�s�)�'M���DA��Wu�to�M���pW�-7����#W߇C�i�T��v��\�g��O,ٟ�D�� GA�K���P�&A����|q���)=���#W2�W<���ԁ8�)9/�/Ӧ)A:�Kxb��^<���^Xm�MIm%�A�#��9RS�0��V�8��;9wF��������K$6I6�TAG�*E����{`f��].����k�I���7UTi[6}ۅR�V/ )I�5�Y&��#W���9�����a.A5��R"G�n�������S���R���v�G[A�h^.���m�j5D+��-ۚ�NNa b _,�#3鸉�K������՞�@H�½P����c�}�e��T�M��{g���?#3V��R�kZ���rn��/�rO� �����*��|b�m��0�1�ʳ��`n�� NP`o W����:E��ƜKE;&�Tx�Na��� �hԻ��~�n�}��-�+��yn|j�6��؃;b���� �R�|^�w��g)Pv�N�8�#W�6W=Uh��,��} �k�ֱ�T���,�h-�m�r��M�=����bM��ǎ1��ѭ��'��+]x�;S��'����59� �O�Hn�s��A�Rh�����8s�g�7�zt=28�����NHM+��v���)Y�������|�N�v���8��{N N�X����<���첣e����\^�����6�Vf��D~+6s_�nN�9c1a��&�X=ejMDG�p���#3�����:szYww/.�sӯZ�a_��*���^/Z��A驔VyL�+k-�\�0H�_e��$��c�V���S�W �{<E;����R$�� y4��פ���N�������U��:�3���~�4�t������gy��G��uQ�w��2�5����A{�\M��uŽP𬵨Z��c'��kmO��/�Jp�j�ۯ��h���8��-I�ý&%rx�j#3��p�K[>*�cä�(.~]#Qw�B%��8��^��ާ�Ëd�_^�=�e��t�]\RT��Fަ颚�,�L�G��˙�i�2x��qz�i��(�R:b�mC�BiB�}�Y��#3�Z,4{�O�\xF�_�Z^�iJ���vQa�[›9���x1�^�5�������m��T��S%�����[��N���Պ�7FG;�.���u��5]����y�M�gLf���E��ާwr��H7��JXIg��[�߂va���C��0T&^��8��&vΥ�(��b��4SH�i{����bG�MV��>���/�n�~�,t�����A_�W ^���TFQ���dx.Ѩc�j0�+�"��C�����S>=�����Sb�ZW#3�0�Ts>��C��^��q���{�QΗ���������_��� �ʿ�O��Л#Wq��ӥ��h%��AAqN�2<���i�b�Y#W�#W���S����:3��gP�#3��,AJ5�s�X��:��C�"�&�E Z��h4^L�� ��(���l�#3�V%+v��B�*�r�b9ʙ���ۘ`TNVjk0*�w �P�4���Ay�dYq�#W���ii�T/��2�����K�������y��4j5R���p�i�%nM�,�X�q ά.�u�%n��s#0��%���1�W �N^W#W��7T/g�W���w��xah}\�V��M�[ǴfT����^Nkmqs�N���N�o��]99/3݇];��|�d%<ڞ�X���u���T�����x��#Wz~�t�5����s���s�3�q�������#3b[�2ö���R�*���N�ZY0�}y딖D��Ѕ�JAL39T���’`��գ�Hm{�qf�U�FB��;�@�{!\�A"�7^��v��W� �qǔa��u�z=}qe 7�+��`Ԫ��9D�6Ò��D�GEсrՕ��z��2�״�_�Y�ve{�Ov8:��,zUH7Zц^9��?,\˅R��}@+�S���^=Gm\��'[;o�=��-��q#WO�)�t�L� UU!O.��i��*�4*��[�m>�yр�Y$��B1�1�wW�}�w{�ns�h�R�+9+=�9���\^������Zʂ+�d|*"XB#0���Y�͵�Պ����H�M���0���ڋ�1#0���%��������w�;U�n:]i���=�.��#W���R��(=fɡ#W#W�0)�~j8��Q ��b�Ԑp��nMz=�#3�ZB(`��t�� ğ7�1ۙl�wT4��K���9,!��(@q�����!���E�PU<ɸ�Q�e6X��F�9� '>;��5��������T*Z_#W#��^Y���0,���U��Pv�sZ\;?�bZS9�|�.�[+����;�R6D$��|QU��Uw������Y���<ן�u�����w^"m��\�j5F�m�8�F#xEq��Nx�Q��zt�����l�y?9؈��aq@��2.�������l�����\�_�h���@�� ���C�Cn��$�� �u)�*P\n���.�"��t��B-"/Y#3́l�� �a� �'��}�<��M�5�ta��F<�v���WI��M۵�ӬS�>������]��mУ]#�&���L�yy���â��tS�r=1��Xux�S:w5�D�p�ZO.}|߫�g��9֊u����}p��ѯ��iA'WXp�_M�?S�e�;|�9.�+_BZ�-�c�� #�6(��&1��;λ)�f����͟1��(��X�^æ�k$&'J�����'nw�r.x��Κ�K�v�2ۦp��U�{��j�%΋y�6ɄD��ڷ*�d�$�9�ln�@ρ�{���gBL�Ҍ"�XA�"ǖ�YIg̡�d��JJ���k����'M�iqφ�q>�⡍�ۄb��;z6z;J9�����8��C������O���x0|�����J�4gW=�����M�_�j�s#W����_oilj��v�a�(�!��v�,�]��JP���Od�+�)�25���w�l/���S�b����K�g��l�^�Y�a�=�ir÷8xނ0�.��!�3LJ`c:�*��O9F�_��D�>>hcƞM +�c�>�^#3�N#d���Zf��t�F�oj�����騼�ؔ��L�_�i �z�} �:��f'�/Ϧ�h��@���2���(���TV>6��R�"!�f#3.T�^�g6��Mp;,b��%4n�s19y�S�~~澚�Q�_M��g:�`���Ͳp2�w�޽1s嫤�Hb�7�H�;t�cͯ^k�1��oZ���eP�@#04 u4���hV�}�6�-�y�@���k�����]�]jg�T#0Bvd�"4���Hg�u�4�|/��ww(x ����duT��yh��z�Nq;|ʧ�S�b������m��kҞ��:���pK#3���9�xW�!HF�ٻ�pO_J�Z3����8u�[Nf�r��GM�:����a*?R���Rp-k�;J�K��O������:Q�]e�A�h�4Ų�Q0�3��ׂ����La�f�*�Cُ�.[/ (�: ���������_��^]% B8}�}.��ZKHtE��?�|P/\a�b(����.m��+���*i��JuC<c�؀IB��a�G��P6h���� T� �j��Xp_Ց���>�?������{�����| ��ǢʸJY׷��<�:�3������@xz���Z{n�}�BTagO�#W��;�x����¸[`b�� �t‰��o;�/���`�,.�/�:���#3T?���"�T�(�)�?�.J��ĤZ�l��ѭPp3J�xN�v��w�Ga�M��5k5��E�Q#7�O]�e�M�<��� ��k�)�~�.�#W�HȤ+X�&�Ç�z�:�n��YIQT?���8�� �3�R�����"�m��f��r��&���v���� Y���0�6u�� ��(�b��sG�Bp(�%�����@~g�L�=Ս��I{��j��_qJ6�zDF�SL5T 7*hl������_k��T=1W��@�����?MtG�#]s��3�#0gd�*���3[����x���/��M�9}A���D#���;��!s�A�H�E�#3ڷHgMV�PH�u~�nތ��6��Ͽ���>#0h��V����Lr�k1�B*����u��u�=f���mT߸K}wt��gL���l��v�s���]�$A������)���ra�?W�T���J��jt�r�7�����^?�A�Ã5�ݿ/zz�Nn���@ *�0[��f�~����=_y*�x.%ZnjҲ5UK��������="�#3aB�jgrX&��d֌E�5r��H<�D,�����_s�<f����SVp��d�<,�Rm�v�B�D 9'{47J.�#WdXZ�J�^�(e���� �2���tCZ�������z��)�a���%!'T���j������%E,lF�{����}XD �p� ��*!�`)�j��ǽC$����RZt���m0|#W��4/��#07�EJ.�� :�"�닲�E��2�l��|��ñ��S��R��N�x�VW9�٘��ʹe�;,U�=�U��h��h$cw��7�P���#W���t�ϫ۾\�Z"i�e�H��,f�ZD���Kz����?c��.e�������k���q���R,Vkw�&&�E�� C��.iT1�B)��|k�ef4�����U�g��%�;)�+ ݢe�u�1�;������=R���q�#a�<��-��d_龻52xi]��+n.X�<���#���+���� �0���\J�ki��"�"7s�ߞQ˯�5�w����|f5���:����MQ=�K�dc�U6/չ������r�r)���LD���0^b#0���cXR@�=�/�ğ2�IV��d�8N���(k6�;�4�( I���'wP6�Qj�e�X�LO�F4�d �t��d��E�NY������W'\C�y�"”u��#09ҳH�d_�f{#�Wn�SFl��� �#0�L+qq��BmB���QYA�>^��»�����{�<րוÚ�~ L��sp���80������v��@��m�Ӈ��pќP�{��Z ��������(�B�㙭��1(K�P�1����Q��s��F����n����蚸�?�����3#WRn�͇�~����{�a���/��0�C>vMR��MRAT��Z�Ad�&��.[*s 8��F-���]v������O����Ŝ����qޅ���W_��T��#3U t6�<�^�9l�F��s;� u�m�����N�cx��N�ǣ��rq�&$x�,�����^SC��\�n�����F���L�/Q�<����2}cС�o��~o"w�q�; |��|���ٝ���C�mP�����y��y3��x<�Yf�X�V����ǸR<��$�P�]v�� K��D���d�>wTA����:�.��6�-������D������o�[�\�%��S��AXt� ��;>#W�L��nzit�k��"�1��(�(���k^�^7� n�����ݹ-�'�1�-v7C��}�1�\$HCL� �ۇhl�{��w6E}�fL^D�F�[���:��X�#3e0��i ���7�������0�]�xPC ���\�����VO��_[؇�-�|�ռC�p7($��X��`�?G#���Ļ6�(N��&Q�U�68A�zmr��Ʌ�#W*��)���k~�[���uV��99M�8n�Q�J�3�#WoY��#3�~#3��U`������@�u=k_:��SeI��YW����alr�l��+���*[Z�?=Q�VPB �.�杼��/*>vCvX<��3�!�~v��=w�����l�Ua���YR+ЌY�d��a��ƈ�,6�`�#3�R!#3`���1Ӳ/ᙚs�pa<�DȀ��$�$u8��E�"��h���@�e�]n!u�t���`��a�++��6t��Vy�P��_(1"��4�e2i蒒��"���Sɼ?�uq��wa�q�|a���5oGD�3����חK���p}�h�0̉� ���g�Yf��K5Q��"ʔ�#0���#W�������CdG ��k�,w}�����ϫZ� �)U2��X�� E��5r�[2�K�KI��B�n#��}C��+㭱��K�z�����Ȯ@6��LO^�sOJ�#��� �J&{��p��|,�S����h����/�)P�HayH:��^GZ���;�t #I4�#3�!r�[B�T3�]��=�=Pt���N*���!M.�r5*���%BIkhf�^�RPY�0!L�p#" ��x�5(JպvL!���SH���H�p�L��7/'��=��\;lFU����A��i������t�~�x���laoa-.�̺�G �&�\��*��i�9���Y�) 07��(djjۓ���2��$��:���N�-�mDqU��j2"썈��^��f�m���/>X˥L�m��J�/[dJgǏ��T�\��#y��1֟#W�ƪ��S獳Z����s˚��s7�2�P��g88��oe��afp�P:w�o� �T�5neb��(<�x�:O)��wܾ��z#W kS��:Hq�/�M���!�Y<':�t���`R��x���S��AR� `g�{�/�KL��g�b�~D#0#� ͛��ՙ:P�ڠr/D��]�z�Cx@���dd�#3��{s:\��R&����㖶�OY#WΑ�t�ȡ�=�% b��,ZH�:�a�(����Y�M׃\��E���H��}�e;���������i3����1�#+i@t�@d�Q#W�"�B] �4�Qe��Ƽ�'�d}o#3�W\~�]1������[t�{�z�Y� ��A��%T;;�F�S�V���}#3^��=���~��>�V��##Wb�U��ۺ�t��.�,��2JU`��?���`yU4�rPú;q3��I���6�@��b%z��ʓ���Yg ��U>:('�"�j����<ڽ{���� !��=U>y���T���#3�-#�E\0�pT2���c�v�'���3�7���xg=�G�"�z��A[�B<�����[�"}#0[����&����Í���Q���GV�N�=S4�bR��.��K�MMѨ������]7-�0�:9�c�ϣS�:�yX��Q8�����/���ue�8R�<�qW��#?�p���7"��a�U��N�V�Fٺ^�E6��oe�w&�V2T�'�r�F=Ix�:x��?����ۿ#����ÐN�XNk�`M�Y �#3E rr���s\�h�-#W� �{#3 ��e�#W���ڃ3?#0�&$��d����=��Πq{��i�O����lx�|_�� �i/-증�l+��!o�h<��Ϯ��#3٢�сP`����\�������Җ���Ão���d���=e0��C܃�?�2������L��I<on[+��$�0<;}���Rb_�� .y�c�#3�h�‹Q#0r#W����s�Y���)�vDro��C�P�"��Z\, u7@� � f !c`l2�^�@���aBf�!$��]�#�F�,,Qt)~v�6�4\,/��0` DC[d֔i�^�c6��#� bXp�,�c#WE�D�"J �����D3��؅ l��y�.��O�ۯGx��T� �C�>aq(�!^���P9�����J�����%�I��;�b�o��\��;4�^ %��T(,A��9$,������=��t���oʊ��X_kF�C�a�Mx�<.�GXdG��4=�u�ז��I������{A���sl�Y%i�����&��4��Yҹ��Q�qm26VN%�� �!��Dj �F�8�! H�4҂��M��a߅a7}�]ɫ �-�j�w�*;J��k����{�x��:�*��/�)�z�:��x]��uK8��.j�G��E61Q�h�/b�vp��E�O/u���&�]uRpd�f�������uDz��i�j"��|����C�a�x6/�[��y�iu��6ީ�����@�k���[=�J;�IR��_3�&��#0�-������a�e�B��&�bWO���2���ˋq�m��*��G����w ���ȍh�iR�i�)�kϲ��04UU#LPZbUC�ۯ�Y�L��g��t;�wg��B�SY�%�,NP����6���} �!�r�h�A�#3S��X�#W+���yX����O��yk�H��������!�l7Q`L�k�&�c�]a��x��}�x1�Cm����͓����x��Jd���d�ȭzR�4�3G���L<�@��+&��b��;+�5�J�m����q�Q�@M��ο68jϭ#W]��z�'d�!�@���i:��\7ָ��q��%�Б=Hy�軑�g �FTC��h�:̅�D���<ad�Zmxw��xd��<�ӳPM� Z�i��^4Us+;�6�:� r�1�"B"��x��Vƭ�Ւڼ��Uv[Ƶ�mW-d֮j�m�" #W��۪�!Xb��ݹ�7^�yۭ����6JM+�:)6VP\�"mpĹ�5wX�Ƃ��釭5/z��ͭ��`Ò��+��f�؏G)��d���g����м@6ċ����=�-���_e��)|z��L)���*��g��H���ZM d�5�Y�E�y�l�����y�%f�Έ���2k|#W��Y<�n� �b�W�A�q�.]�������m�#3�f�#0�}�F(�o���/M97>�u��O��#3���0������sX� ?.ضn�V���n�U�m��ѨM���[�{Ĵj�^��x,m�4� UT�����µ����:��;'oP2x� UK�xv`#V&�=@�uڤ����ႋ���M���3����{����c@�Q�;#3 �X�!���'޿b}{���y�T���e�W�� S�.ųɋ3��9�dV1�r #0��G��ێ[�5 w���z�a�M�-5��$�:�^��(5��H��{� �LU����yf�<y^}�Pt�#W��L) Z@D�`��ֆ��e8JV*M�PB0�ϖ�Nފ�χ~�X�Cp�6�xpG�_�H�ucn�)@s&+�k'�T�r��>�����9���j�?$�ƖE��B���aEa��\���=yy�w.�WwJl� �1�Ŋ�F��|/Ǵ$��;A QS�e~TpO�g���\�6��d�Y4(�Wz��9��K��z��|�*7�ʧ*J�{�q `Ύ�o�5I)�,�,���oA�dRw=FN<��&���45��6F���D���ߪ��-��y�k��Q|�w�ho<�M��OZ-x�j��f4�5BI0� �~T���������͌��Mx����8o#3I�aӈO_#3y�)u���K7��o(��]���F�$`����1�Qu���.��}�������cO��ݿJv�,�'����C[�Q��WT�J�d�-�Ξ0�>��pzt��6�2�m D�!�[C~ QA�;:t�}ڴ���<#W!��sg�^���z+��'[����2Fǡ��*���٢�SK)ݮ)+����.��Ѧ"�n�0^ %�A��?�x��=�(�Ӑ�q3�д�}�{C[I�}|\�)�T����'�� ��,��w_�n�0�͚g#W��D�iE�q-SCaA%�"�O3V�'[89�Dɥౖ� _�t2�8]���e�D"C���;b�UK�hw(�s#3)��e9������Y#3 't�t��=�k��yL��gxC�dH� ���%Ĵ81�R�����`�Ռ�>�m}�����Z��ev��{㸾N�#3�uX�d� ^�m<Y�s>����Xח�Qu���O���h��� �9:�Y�1-�H �� F�$�u!d�c}h_�!���� 2:�߃i�`�����>?��'�s ��6K��,���̺w���y���]�J� .���N��ѾA�H�FE��K�fq:�8 �=�*�jS���K�(c!�Ӓ�� ,�Ԝ+u�e�p�R@�B �/ow��{d�W@+*Ta�G`��V C�Ԇ��և��~�sG��Ȟ"�r�H���gC���\90_�3����,h��G�s>���=���tJ#3�ݷț�w~2l�"Z�s���ld@��9��&�X��)T�#3���!UX��ӯP�GB��±C]q�N+ѯ��agCoo!�3��5�𚶷$¡� A�Q��6o�j#0�i�ϑ��yL�.J�z���g`�L�'&��'�=gE��r���xs��D$-�����x��$�}ќN������܈:lx�bq Ȼ�rU��b�wh*�%EI麨�k �MU�B�7a��4�V*T*�ft5�K<=�����*C�G� @�S��8���ٌ���=v��\� �n�&�M��G�N����X(�Ԅ��,ׅK-<*x,��؝�=�8>_��㡥��"��{:4%��0��-��E�'���=U�a��>[��n<j���@�a[��*M ����W�h�&�����4c�;�������� �K�<<X�����F�����كD���a���a(y�Q�]n�\���Ɖ�A7�����O�F�g���f��{�V��pt����8j����P���/JdJ��P�D�����e��_�A�ʎ��\�����PN�Ӟ� �����TM�0<�2�-�~��]ߏzUu'G0���h��4V�[���� sxnX��SE;A����}� ����p�#0���]GաKd���g8���QR�SUӯe� )1r�a�ֽ� A��P��L�z�=l����"��F���;#W?/����z��@���d�#3"HAY�%H� V�߿�#3�̛o��ވ��_)���J���r��J���%��J��Vj8^������P���M��SdQ�7��bC��0���N�3p�ޥ6A@@#0R�!�#0����>_��?��� �¦ ���c#W��P��k,_Z�Mi&s�����{5�ep���}w�*O���t�m���`A����EL\˼�D5B�&�5�n���u����C/v��z��Ҷn�(��aZ�#W�o��+J�;�� �C�R�UTR\@���������s� �N������N)#EL���V~ӳo)��]ZC���l?|]�*�"��lmG��y��Ǔdst�����>��o��'1(I*�����W�J���.�%�0�ц�Ьbg�@�Xp��eٕͰDl��9����U�Z�bQ�NY��ˈ����V�J ��v��yn�FΙ�y�G��%���6�F���/��S�<:+�͘�t��sDO#0@H��dj�#0Ԓ�ٛg�݆�m�Gj#05R�"~��$`����_�rgiw�������1� �]��}����<��X���5_��.�`�%Mc�u}��=HMS©��� �>�Ǘ�u.��ER����'^H }�L�8��0�b2%/�f/�m_�Ϡ���yON��h�@B".�2��9�&\�B)E$��˜�Mf��0�d��t9o�#3?���e���ڃ�}��/$c�Edd��<9{2��r�U�-Í&�9i}��]�S#W��*����l�|�|�,����d�9&n\�J'��r��#0�(�"�u�x����Ź�հtw:��^LNQt�.�Q��#Te(Lx�0��j���&�3b�r��o�rkV�������m���Um������2�������ͩ�)O*#0u�?����I�r�#0�u�Ap�ͧ��}�ȗ���� P� �b����_�.������!M(��1��������^Q�w�^��&���L�O���˟�7.ώ��{�^�<{v���j��n�V+�H#02=޺6�;�_��#�{_�����4W�v��)d|2x�=�ьߖ�wm�;�Q��4��� G�D��|ȕ����̀�/YA©@:�վh����c�(%�$Uh��ɔ#0R�W�#0;1�l�$�G�㥼����#3H��.�#38���B#0���Wg�c�B�(P#3^�GZ����U��k��!�+O*��}j��(���-6#3��X��U���q���#3�A-����ʜ���p-��w�N�d()*��f�H^�WÏ>|b�����;�#3y���H���MW���`�S�1�z��q_�Q��P��,���$��ԯ��$)g����w_!J�L��kzs���ۡ�@��g�� �;��7��I,���߿(l�S,��Ѧ�&˲ʽ+���}��?#>������&z�f��6�3'Gy��'�Tǝ�?O����Ӧs"o���Ɲ��={�1 � R��*[��w��$��=xA*})�?H�DC��:�L� ~��h����o�.<��O�;;{����1�y%T�#W�o�F�C�уmnoc�����L�o^ɰc�j�G�*�-,p��q�,ݫt�V#3.��I#0#0>".�1GԀ#0!L�p߼$�)�nw�5��xt�}����PL���=�a�dx�� zxg�~=g��|���9�Q[:O�0ډ�e�yB�sen�tvK-D�"�:��q��M�<��$w݆����嚠@�����E��ߝ a幑�#0��#3�A�)#�dء�=4��>��I�+�BU�l-���g5p�\<���J�K���t��v��]j�u^x!�2*&�Y�{NxE�)���rF��F�~��ǿ�������>mz��KV�� G5�#W#3�0fH�_�zI�΅��:b�a�G���oq���1�Qq;�j��V���f����k���R^[A���o_��u��uC�r�ˢ�G�:�ל]R~���Eo{{�9~��Fe_ih�S�� ����@NJ�2#W�+����pD��P��S5�am2�J��m3F��=��T��xz�<=��)Pf#WK��Du�k_����7܇^����MG�:���/�����{n���xucb�z�D#(���̲ғJ�����X¦�R�D�VeWB�#0�����+�U0($ߊ�3yay�(�DŽ+e�AQ�@n��>{�Y^3r��o���ol�P5*�<6꾘~΋���?�eVǞ#3odž�犆v�i��7{�j3s�u����X(��+ȱ!�#3a z/(N\GR���/lz����)�-/����W勊L�G*�~�Y�r+�;l'�GdB�42�_�k�AV�������Z�W�Z&�_UƑ�2{��;�A#N����I�*�,�K��ث#�؎"�wѴ�@�w�v�ʽ6��qA�b�T>��A#W�_4�=Gw�:3'so1�5�������������ƳQ~*p���(��\w��Ψz��?tf�5`��稯~�Y�ш��C��U�wֿ��2��0��7��H!.��L&�L����w�$�Il����Ժg{���-���:�v����b� ��H�m��#��7�9�ʌHYmh�d��(���8TV�h �]/pҪ ��k٤#W���;�7#�f���)$w2-d�����J�g��{WK�]�nCL����l`��1#Wa���(��������q����-��9D��_hi�S�D�_��o�G}�j�����;��s��)l�g��;C�[K�vu� ��s�3��!ϯҏ‡IWM;����i=`ݻ��ɩ1����f_^����Y[P�6��So�.x�[�{΋c�:ñ��:I;��ᢑ����Sm�fr]��;����BW8i�6�)��p�,NWs�������_u<���c�3J`�Ή��o] ����,:�jJIN<���C�2�l̪]�!ɇ�y��t�#Wr�'�k5^O�签Z���Q�R�)c�#0��~P#WfN��#vu���Մ-z����U��ɳmTx�Ÿ���GZ�ӚU��B#�&���d�j�鋨����:�lh�z_��}Ցu@)%���[���Ybix�ў ��TR�%��?����pD�'-ˣ�ǑY����nI�_��Sd|pt�F,��'l7.I��J���@*��fu���e�+ЫKsi��X@-�uPtb��ϓ����_[=��Q������o�5�pD��K��)v���;���'drm~V�n?��&��J������y� �j��f���`�p��h,Ք'?�����{���1���A<���B�^� N}ږx�_6`�֫���y�Mm+c�zߙ�9��*���X@P?��$�gS���k� �WR ��c2ѣ2xY!ik��S�C�woa�җ$�������|e��#31��lޞDBj[��I��8�G�`���IN? �?&�52;%o�|C&؜����o��…�g5!�y�e��A+%n�M�5��N�W�D ��6������y������~��4tŝ�Sv�R초^6�"�s�^������� �\:dNz����]�o$�a��k�߮_1v�g#0U���~#���L��KB��~� ����?��#W|���>=����d é#31F/ ���܆���;�?]�v��?>h�>��}��|�׋����� ��t���w������&�������� ×M�Y�څ�zf:'C~N�.ϔ��M�#3q��d�����O�:�D����l�|y�#3���U8��^��(�~)jw^Hm/#3�����]礇�v50��@���s�v�!����vg�����e̝쒃�賙���^2L.X��U�BdFe�DV%ف�k�&~H� ��s��h��S����w#W��������kE��e����C�4®�*DOVa�l<�����B�E&��R��|zY���\aߧ������Z�����#��E���ǚw�Åܤ}�u��@/N#W(�(`š�P�#W�R�[HH� W�P��x�eUլ!�HL!���@#W�.��ELC��:���F���t��N�>#3U�#WG�P3}||�7���(� J��T@�awۼ����鯺<8�Q��އ��a<�A�x̐���rZ#3���y�~3^w��_x�ނb�h�|c�,�B#0G���2xE��Wq:߳?e��~�u-��-����߷]$ф��s?a�o���̆&F���$}������5F���p�����8dR,x���Y(�%������<~f�V>e_�$����/VZ ֬w��a?�{At a�xu(�*�n�Q�(�d�!p�@��������+i��d@7"l6��_�|C�|>e�D���iP-���=��^�#3���ۘ�#�����*��:�����Go�y9>� ���#)a�6�+�g��=�i̝E$ᔎnp�pϜxb�����Ϫ <���O1�v��S� m��Q��Ow�OO��Kg�Rp�̋��0����b(� �� �D&@�HO��`Ȉ�#0��?�����}�Z�(���F�����G�����eO�U��}4z�7=B^�r N�� �\����kX��rYyq,�)�a������Y�b����a�?q�;��H�|y�"�1��7ڟ0Hr�-��Z;�����ϕo�q���I4�=�#0�� ��)N�9��#� u�,l�bX�Ľmf�:���(�=8��<�|��K��[���QS}���Pk�8*��*9�#WeT�=S����s���}�q��3�:��Ӱka$�ls�ژ�澔���A�~_-/��N[&1� Ar#0J#0 @0�e�bT;;>x]s#0����\u6~�Ϟ���5 wɞ*˪T!� ΢�T�� ���זRZ��R|G�v#3�4�Q���P�#0G$�$��c0���T�]�b�Xf&i�� \џ<V�Z *]�?�9#0��q7�x���I��=&�3�ɯs��LϿ���k��:r�O�dyھ�ա����#0.pQ�.�,��+!f��R��@�r��(*����0�eV�9 �+�^���BDYf����b�/kPK���9X�w��GN�G��yɀ�>�.8��-�s4Ͳ��k�'��߻0�[��vW ~���s-��R�tA�tcˣڝ5��,p���E8�D��U�N��V�7��]n��0��3��9����m����wo�/�6�\�%��N�?��^Cd�^>��-*#3�Z�o[P�>�:;�`>�]�x�)�F(�[��?=�oJl殡�[,�:������E�~Nj�g��&�:�E�j��H���t�t'S�ͼԂ7U� я\�Ww��6�� ����S #0Rb<�R�>��5����G|KG*�!�C�4��Ĺ� �M���0I� |0x^r7��r�aGlY���p�q#3�d� F �]8R���̿ө:l���m�4>��a�r�� .|ޙ���{G�_�gSqr��U�t<Ќ�B���őI��T~�ѓM�}0��߃#W� �&I��u�v���! ��h]��>�Q��M��J�ݝ�jϩ%a1�CŹ��~��,f�G;�'V�X��y!�/s cEVY�v�H�r chy�V�|��(��������&L�!_}ͩ6�d�M�[�b�SN��G�+U.І��{�k�z�;����m�qˑi^�?`� ��2� ɡ �X7@H�жp��p��6�JǭK4�*��w�ഴ[�wʾ6�ۿ �]}5Ľ�v}����y�JZ��S{\\����� AN�Yr�_�∗�QiuP�M�ݦ�4����0�3�-� ��~\�=Pi����kgg{9m:j�0:������mW������U�ێe�k.��|�#W��@�uY�]T���u���-A�8���7�F���$�j��sm�ap{�_�Ǣf�� ���M�kVy��3�뾱�����F�5���XN�e��{��!�_^d��tM��h���y���sVU�93X���U�X�*�M[iH�m#0�u6g��ʊ��� ���g�=�!>��w)�bܳ-Ѿ�� �n��"�)� @��62�tx#P�#��]p/F<��_Z�;n�7� Kx�}����GtZm<Sn����C���l|vm���F�>��1s]�Q����,B�#0�����d������b�2#zzՐ80�z��#W>K}e��jr箃�#3[;p�B���Ҝ:y��/k{�]V�>����;���C�����[�.�����D҆��eN�ځ�6g��Z<݇#W�0&��䩞�q�]���#3\���#W���O{��MÏӈ��9��#���v?�Bb��&E�힆�l;>�̊��� �#W@I��ߢ-�?� ��}��gL{�mZ����f�Z�Lʝy\�V ��k���=t{KE^Z�7��UV���؀A�A���)�h��5M����϶���k4E�� >_o�7>�x("�-��"�lȱ��Eh�h�mŹ8"�G�sd@T0�&�aW��'�Y����)�ׂS��̞:Ҟzv��Ǫ�]-\�� ,�Pu��5Ƣ�z�B��My�=����%���p#W��x�9B�T�9o��f�p�QĐJI<�ܐ#0UN���p���i���lۇ;����h!Q�n���:>�7�ܵ�Dh��#0�� � ����v#�[E�lW�q�����%�ge���Z���s������'1(#0岇�OCպ��{�`F���xM�>�_�s|��������2�T����#W��:��ӍOǃ'����`gh�\�ga�Vk�����^ %L��e۞ݘp�E�5N��u���t((j �_kP�$�o3��":MP��³��[<�3���v�檸�4��r�޾� �ь�ۋ������u�U�1K�T��u���[ܽ���D �F����%��Q�����W�(l��,�P�/��Ʌw�Ze�ڬX�#3]��a������i,E�`$��2�Y+璉�c!{� �{3�jr�P."�0l!A��F�f��D����O~�\�ʫ��T�����R�����^i��G�;-��䈔�b1��'�n Ȳ�wT`�����>C��#W�x1�x� uef��d�&���T�%O�+��I ߋ: �e�Lu��#�҂u�~�z߾�uَ�O���� �a���&t��#i�[�o[��+�0��C8���A SZ�ࢷ�� >����+��k�ū!�����u�k�Q1Ȗ�����k�ҡ~s�+5Ƽ��M����­ez$��Q{N���xRj�A�TP���{����,�ת���m������o6��)!�6R`,�(+B<ll�M����Na-� �����GR�o� �u;?8�ʩJ���Jv� �5{�]�m.�#W�Nʖ@�-���W!�z�3��#��W4�uȖ*�� ʾ�fz�*��;�:W*��9�HaiV�����}��h���h&)u\���5��Ns�V�,f�#34@|P3�۳#W^��L���NA$�2z֣|�|���\�lV�D4m��3r !ϳ�33�G(��wq8B�O\#3�X⡕ć�.gf�^uū6Z�s�d �l���}hږ���"�H���5�{V���h���$<A\�F�(�ε}i�V�Z#3m~��5���������"wB���UCB�Ò$�#0e2���:�h2P��S~(�Y�!��#W�;`�8N)����J��Di��r#3X���w"��o�K�\�q�̅�_� Mu^Q��G>��m�M��mGl�hǔ;[{�)��h��H�2�nq�i�)�=؋q#W�:]eh�|0�t/�6WE�C��^��^���Z��;w�j��w���Է��,�3sP^��l�]���cX���Z�vZ�r\n�ps�&5Ռ0��Z�U�-J�+�n�qQ\f7w[ D�"��z`��hF��:^�#0�@��4taa���(-��5Ơ�h�zZ��>_#W.�W����99���uA��tHx��`�]y�v���忍۰Q�=#W�d��LBw��P����B��y��E����G06��Ot�c��W����ݧԬyr{���hzey��o4D ��i�w߼Jo��Dܠ������\�qsZ@R������?W��=�!��3�Hڃ��_�wd�pB(�3� #�+Vw�vt*�TM%ev3ؿ�\�r!P9V�����#00y*���(�t�����I�����N ���b��u|�n3�Yc�;�#0 ?��K��p;u���#0|!﫫�.�!j3�e`ρ�ULU�~������߬|�W���I� 6c����m��-��W��LV����~&��z�A 4��dr~��S; �{�#0ޫ~ܷ�j?G���` 6P*�eR(N��8��c�P�/�� ��7�� �#0��M?�#0�#0P��#0��#0\�;�oP�A���H ������O�,�� JYUC���j��<?�A#F�x�v�\�Y�2b�t��!�����)[?����m<����X��]�۠tI/����F���5[��\�����ގ��gӱ���p5#W&`퓵.A�u���q#W��q��˪�3�����Hc��7����uE�#0Q���۶·`K�D]�a�Xfc:3%�����1��xu���9�����}��}.z����GY��#� h �0?�#0[����.����&��e�90�˟�wXf��4��XX����5���2 �s���'�a�^C�H!��,*&_w�^ͧ�u�u8%7��$ ��3�벫�Kl���"�5B��C��Hs�����]�5�~�<'�M�G�m��OP�\���x>�� �� �Y*%qN��h�?�#0�w����VZA����c �;�����������u~�_���Z��|C���C!���0�4�d�UV�,]�!p�����[S/�-A����Z�I;J��#WKp/r��6 !q��A����(o��������BE� @��.B��)э�p�e#wNbЯ�n%@��`�Ԗ�Fr!˞���xZ��b�� #W2���#3��\cJwtI�,r�+�!Q�ϯJj�Y�9rO_˸;@٬��-r�����@ax]���7-���ʚ���j6����J:��C�k��m�э�H��Idc',@R%�'Rs��#0�s��-� JQ�. �_ٲ"���K����.�#3�nFl�Н�a-��Z|�?D�_���'�@4"��T�y�^���>�/rx���혏�*�&����;�i�d�x� zh���"s�Z#0��}`#0XِWX�P$����'_�k�=��d]ъm���� �p�����v�]d� �*=�2:,y�����{��a���f�&$Lj� �$���o�Տ3����_�ƀ�ʯm��=����#3o�n�;<_���=�]X��Q8�nл�ꆈ�"U�bgH�!r�\"r���{qt�#0w����_�2$O~�;��X4InjV A���5��yG�� (�ݡ{R@��KH2Op�{|N=.���COW�*��"�9~1�S��$�a�%���movN��`:D���?�4�jt̳a�?b�r���'�^���Fn� ��*��LB�a��N^�r�\t�b�����I,��wT�I$,^b 0����vja�uUרn#0�����̴��@S����ܙA�TJ�F�􍡱���2 v<�#0��7�g'`��C�٫[0�K6�̢��)�D:��ݮ{�@|NW2?��}��-����do�� ��������ӑ�B�b�x�񿰸^��?��'�k1i��������sF�3�#Z��x��A���_H� ���������R�~'c �aX�f����}`T%�GqK���#3l��%/�K#W1��&��Kŝ�u��^h:���`����Ģ��}��#3+,)J"��$h�����250�7�x�rr`�" �cA���ٴ?p�L��n򏗐d�#3u �!���d ��W���46�����Y�E#32+ϩM����s�����!�\��uk r�]�@D�T<�HJ�����d��Ȗ'�K{�&Œ7n��a~(t���dI�\H~1usN*�x�.�F,�4ԜDm��,��8���#WhԦ� fflT3,��Fwt�ww��;/�#3F�f���z��a��B�&����*����������.Y��s�]*@#��C�Xsd ���"�,�&�a��Ɂ���L75� h&���]_��?a�?�ߊ�#0mC����޴�2�`4d����XZ�-'���T�ђ�/]����"#3�`���R����]�F�KK�F�2��WYY�Y��2'�6C� k9 Z]`�:�2�JD1Y7k�$�I3Uk���Z�����O3W5e�Y�L���:�P�^�������&��j�45� r��G��V^,F����eA)4V3��+�_�퐨ŀ��#0� �fJR<H���fVi?����zPl�s��܃����;�O:����Q�!�aR!�' �p�v�J��"�KX�0�x'̠m #0�gwZ}B���;�vv�IԴ��wz�7���S�>V���n#0DPBg��gMq.�C��w�44��`}�r�yJ�ZT�F�p�Ql�Щ$��e���H����$�m> ��wt����A�d��mƱ��J���$����|���\�����!�c�^n��M��p۫�<8�~%<����z"��)���ۓ�/��X=��e�{�Ss�#3#3O8S��͋68��|��]9�K���nf)$�"�)O:�W뮜f�JW(�4��0"�$����,�_��=������"#0��< �R)�Q#WL_��@\��Q@��_��a;��y��w AH��, ��)H�2�V'���=:��wd��\#W��WP� -�:f9;.v����Z��HBCf��jh���52�I��6�#0w@�٭,�^( JP>igJL�Ͱ��ZsJ�#3�+�P��Y���}�,PO�I���'k��=D ��E��q#0bd/Ai!�LRѐN�(o@d�! Y�bx�� �5��y���qm�;Ϧ���@���`G� .,�;7�M�>�-�#W�#0 K�|}�Y.�8�]�%B�m�7�|l�u����Оǰ�D�|MD�}X��$���=�#W�OtTS�Ȟr��ĩRaz�}�nL~�=0}?s����=�{��t���C�g�6��?#3�s7fȡ`1#0yΤ���g��tn|N�Aτ:�1� ;�;��� ym-gH�R$ �n��'i��N�*PD����˴Q&&iF���`�E=[�t�w����.持��;�]BV�X;3�ޯ�B��?�9v���!����b��4�(�&la5a>��BεQ�.��#W>�"�M�:�w'�������#3���/8�t $��{t�P��+7��#0bx�v�|�Pr�������(n>�����6�+&$ K�Ƈ�?&W|L�{��/�뇫_U�@�A�5�@�)u�BCՈ�$�܁t��}h�7~�jr��u#3���"@O��_đI>���9l j5v�����s���>��0G�L��u"�5��������{���4��J��ﰝ耛-��zn����U�����8#3A��o0v����ϡU��{�`�*��'���J'Y:��}� :�M�,@��5�#0�Z�>FH8�;aݲX���2fK�M�te��<!FJ��h�DF��T}$͜�8�_ƆPgRG߿'� pde�#WTN[;�*2`�ҫ����̈���1�ID� 8������hѓ1k�[��a����%��z��MO���6��s"��8�x�� � �+ }�D�)�_��=��z%ؖ4Hm��?NӼ�O�#Uʂ�Q��X5˿yӽ��t��H�x���>y�a��� �5�P��6����0��Imn#3 E#Wx� 2�Q{�����S쯒~X���#W��/�LʨD.��-�����~-$������e��ܪ���Ԃ������c��$� �@��#q�R�2C��������U�?����+�|�hM�v�9D[2i}'��Oj�o���/Hkl50� ��g�w!��Vx��bvl��⧴#W�bBv Ǚ������yH#0��E�T�#W��ŨϚ�)��#0���P� .��uR�Gę��7夜���{b��Gm�&�5�.��3�e+��E<�0)��n�7��`�=��0�JP��P�Z6�y���hGY�K���YBs��������9돬�-%�yY��I�g�X�H��:`-��Wr�d��sUoX�I~�bT�e�@b��L�����L�<���TC�Q�, ��\�=c��#p�g( ��e��&\PP��j��� ��"�P������.��(������A�6#0�3��C�M�~���3�09�M�����%��X(�*�����SJR��#W��#WA�’E:o�#3�S�?Jw�5)��(����������ĉ ����O��i��#W���j�MR$R+�E�?o�D�<-_:6�`0���r�j)T6B������6�&�sU�n��n�l��k-Kn��̺��v�H�� Sw��)�vG�����K�@Q�982��=���>���h(w���ɘ��I#0��X��!�0p�=>g�0���4��Ӭ���-f!�@� ":9!b z�$c$@�F�( �& 8.p� �#0C�#3��Q/��#3Žd=�_{@yW�l����T8DQ#3�N�{s��^���J�!K�w� kz�:�+�p��r�qYø31�C�#0ÊC�Ga�ٴ�i��ø96N|��騝~��mv�������`��LIq�� i#W��a���2�wd��{?�ې_.�'�u~�l��=!������Dʢ�D�����>��`/�ZT�9��)��>���֢B�M���}Z;���P��_ m�Љ���ZZxa�j�CӐ�,)$�ԳԲJ�I#�� ��>�����h��4 4��jbA�̩hx��E�H����;����,�U"�uO�+���E/�E�B=�ud��Q�2rB��?ʽ��!�ٱH�T��4Ձ!����e�@uu|���*KM��6_~�ya� � ����˫\S�%�:��=Q���#3�{�QW�Sy�Jn_#3�����<�Ƅ<�F�<Ot�����t�$VF1AIg�;�W�E�^�ۣf��#3����c)�4O���� �j����h���s�>��#0tb�-��L}��}���i�S����zOg�3��=r��@ŔQP}�*�G�s� ���G��� E�~��������Uz�Wz�Ci M�;���!���qXe!Ŕ�� jۘ�����"��"���G����/��q�����uil�k�t�R)K�B��08��@� �79��#3E�@�nr�QԚ�MC�����p�?L.����8#�M��{����S!R"������#0���D���&���t��>����N����󁳍$�i#$B�t�A�x���O#0�X�鹺�r�b*�6�0d����3�]DQVL��TE��x����o�$�e8y�/_,|���pgq̃6`#W���4��^Y�Â�"�����v1d�NJ���nb=f�/�v Ǵ4�d�ٺJ@�دWϷk�$�bi��ù!B\L���lNWv��x׻D�iٙ�%K�wlqȇǯj7p|O��O�9�q�qF �g�wp��h�EC�J��M�sEn�0�6��楇Jf��(A�|��o*8�h�F���>�bX�:�>��@xz<{pv��d�{�Gsx5֝휉r(�M��\������#4�R�y#W!_�A�]zr�.�����]����utؐ�I������t���#3���׫:15#ٷ0·�T5��D���9�[�#Єc�A��F�,1@)X'#���A���Q ���AW�>{Uh�؉6e�x�Ul=�^�%q�9fC�i�]E��2�t?���^��#0=��(�x�]��3�G���1#3�$�Y�A|(��h�<&J��F�A`���K�I�v�y����O��ör��#0(DT��<���p�D����=�A�,@h��<mۤ+� E4�T�!��\�p��;;���NROA_8S�SI���lbZD�?ҋ�x/���=�N~���s9��1Fry���wgY�~����9#3q��Wl�����^���v#3�Go�a�7<)υ8��)" vH��S �v�����:�}��]%����x���{����/�UI$����M�X�J��� ��6�Ɂ���$:n�����Py��=<�ʽ���f��!��8��|b�H$`�~��G�;�� )�z���L�$�dH��W�H#0�5z^����������o���C1���]}�����V/�^ibt��nB�Ӄ2��nP/�r2'�G�^mʅj�;쁱�p ���B����8�gcq�@Q���hu2lj��7��7V*�H���1#3a^Ϸ����C���@���Z��J����z�)j���y۴<J�#0O�?#3G�oHd� \5��Q�����g*��H���I�G™vE�<pj�&��i)��|��1�)��PS(c�?;%gI��S?帐��IS���W�����1o�׮}�s�Ji�0��,X0R~L��BjG�d𚺹z:�+��'#�'4�=LAA�ć: C�G@0�B\"_$�=掜N(�#06�2f�.�u��y�)��ݏ���?��G>��BBM�#WI#W��È�:ȳp�m��_�� �����6�?�尜���$|+��D$�B2��v���~Ȧ�#0�d��1V1������-cp��J���E�bZ� ѣ�5��ODJG6#3��T�c�Փ���̔��`E�d �� c4�����+�r�~j������B�zF$~ir~'�Q�#W��=��hj�O4�V�M}( As!���X��{_i��rx}�bO�R��&�m>�Qr��֞�T�6R��?#0�HY��W�V=�}��������;6C� }�z�|K������J����������#8ү��@}ί߯������Y�-{��M��`�bM��o�+0���Y���0^�����Ti������T���Wi������'��؊H*�0����/��\a�: 0��^>���/����lnWdKy}{�#�9����?M��~��2��Du����z���^���-���ɉ2}#0w���C�Z$H�E�m�Y�#3�l®l�K�_K�:��_h���?�C�Q��=��G�W.1�ʅ���$��H�?��?���#���S!�VRYBl�+Ʀo9�L�d�3��$��TY(����P�EKd0 hbli����F�]P����0W2���N8�1`E��``�TL� �CT�i5X ���P�?�j��g!�#W�����OP� 3fI^ ���8��@����ƀ���X�/��̬9֛z�-C��(b�����e��(i68P�����A��$P0M��-h\0���Gb?��f!��#�{+>4���$m��m>hN�XT��C��I��~ϋ�VL3�"��a!Y�y�����ڴ:��S0�pg2*AS/�#0�ꟙX����uB<��갬W�i���g�����O]^� fu�׳�L����T���h#0� ��>��c��D.��j�8�/�$�����/���Pzv�;�oy�<���:�P� ۵5-�b _6�Sa�v�UJ�".z����CN�s�� � �P�;\��[�ю�t������/J4T����v��Թ���_+o� j�3�g���F�D�h@�R�����qI2͘@�@�b���`�7U��_�����}��0P�}V�b��u6de����P�5>E`��K�9�����ͪ� =E��1��u�ձ����u�zN�y��иs7����CO1X��,�w#3o�Q��7D�%�~���Ӿ|q�U2o�F��GJ��sJ4�Bm�����Ĭ�kF��_�l�g �{E�`��{g�t��S��{ͱ]�QЏ�,�Κ�pSz%[��LE33�N����>�c�4���#3�M/�y��x�3��X��̓���)h/U`�5�9�f,f?�P�+��"����EƄm���qw=�$�2�����o�kw������Dr��}��m�g�އ�،�����U�@��"#3�{|R���,%0!P�B�����#WB5&����"/����=�?!9@g��m��w�O��U_��y#Tw�#3��K�{�|ߎ��/�4s�����>����n>���A�J�$��'c>i˯�`����4 ��i�@����P������#��le!����ĔH�֡^G��EOz���c#3 �����<���!^�jC�?x8�?�6���:�W���P0��/�hf9�P��8�QGO4�~�(�M��s�ߑ�c�6c^�:�π�T���{�#0�U=�qX �]��#3��H����#3����{���;�v�E�7Qdy��jƈ�ft����:'���~�����$*�EN�#Wq}����� �lm#3X�B#0؁�?�"���="���$i+ިqrѳ+�5��KF+�zju-Ū�P�GݍC@h��tA�(�k����u��@RI~�[�˚�j!ԠbbBl�l��)�b���5�\�m;VU1�n���o=��n��UƇ3���?�o��#3z�Y/)�On:R�n�Q���� ^�me����Wu�,�p���p���G�êl#ۗ��t�%c�y�H4��M��j�6�,��\d����q��е���}�lT�eb�H��M��6E%�K��0�*#Wd�>t�b#3.SJ��K�����w:e�g)�i�/-F��m�� ̧����)C��IR�M3F�����l�D'*�y'-yo���[�ؒ�Uݜ�#�<T-j��l�("p�/�2�C#3ٵ8��#��`R�F���9��#0X#0��E8���{�]�K�ij: �E{2��H�#����0a:[¤v�z�����E�w���"�#���MxM��~ݪˆ<�;�:��Z����9xwU�o�%"���0e�4>���M��^':��m#W���3K�J�a�[�_�_Oz�r�t��p�_�]���w��HfM�$adv0�������F��G��鸺�1��V�5�&���R���c% �-8��;��G#NoLv����o�O�WPY�!FS�k0�U����6�>Nϛ�C�O��N��[oz��/ ��.��m�Z�q�y9��#0��;��B�6�e�̠�O����]%'�{_m�i��d/�P\�j������2��+6������8'�ԣ�0�ڇ)Sg˧��}����|�4���J��SD� Dq��""#פ.{�x��O$�ߏ��{������;StR|&����ӑ�ߪgD���4�ǁG��t�=���@������ɡ{�Tɦ��6d�{еC��� t���w�z��b}]�K`Ԛ)�����4����|���{Q,�#3ou���J)}��ߓh�3�o$E�U"a��L��#��'oV|^r�;�n�m�����[{�<!�M��}Ȯ$�N.��$6r�u��+U"�5�Kh�<�1B���Qțt�����%����4�_�hс� L������g�#3�QȱE�wDBFE>������|��W��j��aPU+����#3�o`���JI#0ks�8�r��H���u�����Y�;��ju��Yd���/?ʻ�e#0�C&RD�(�-x?ov���>�����#q�x&��ބ�f����2ފ�����#u0`?��A��N 6�[hF灓@�W�f:O��f��o&��`t�� 6�#W3n7Ȕq�Qr4�R$b�F?��O���s���o*U��g��$�GlF��v���@�� 7۱�ݸ*�Miܦ� ��5�h�y��gĖ.-ƾ�0㾔G�Վ%���n���� ��Ȟ�x�W=zK��Ll�#G�$1=�!�S"SDk3�,̆�V��!��q�M��"%#3�R�T�4����j#3Cx�ɠ$�3�_F����B�'���"W��[��rr2�b�I�Y�G�x��>�Q�y�<�B��}9 ͜tОe���(A���������a�O�bk��*=S!�޼��G��U��ũgO���r 0��1��P��bhF# .�Pf����<8�(�]��Tn�>��!b+Q�a]Kq̝�n���Ŋ,QDT�Nr�hR������s��k3��B-!ؗ�ӚLK�d�OJn< �_�Y#0<��#z6�|\w�� #Wvh��j�s��Λ�-���zx��9>1���Eyf���|�&#0��h� (�~�=M=��vA���J-d�0�]�ƅ�2[R�wi"�#WD�%�b��� 7,��)� *�R����ۓ"K��O���oi�-��F��o�o{�/5�l�?�#37�#3�*�d`���mOxR��9B>�B0Z������h7�����"#WC�L�'���5�I�j�#W<z�����z�5^%TV"#��;G�����pvaRKC5� .��C������j#3,#rH�\Ea��H��N��|b�j��Œ��3�G�v�K����9C4��#3� �c|�e{B�� '29�É�d�=�T�/Mgx}ɰ�0�<�ma�Z��T���Da�#3#CL`lj#3�97�6�h�A�lCn^W�m�����C~�e������L������n�Ӭ�0�I��s�M�79�.E�#W���C��0k���gR�wG���J������/K#W�_�#W�[�B����7�Y4!���Y#3K�̝W@8�5��K����Cr�}�,� ���'�z�:0��7���c /�0a��z��gB^䃽SWE��l�)d=����n��t�HY��-5~gw�|;�߻�28�<G�OM]�,���{0�w��0�7c�䮻5m�"$Ӑ��2#3�����Bf�M�ݹo�`�:���@�3r��j哀o�Nj�pxv�W���M�= ���E �E)�ŷg���1������ �!��n�3s �@�Ms`��>©���z��5�{�: ��;��3KO�W2=2C_O���\��{z�z�H�=Ռ�I��#3WG��(���[[#3�#0�p?çr�:s}�N�� ;tPk* Q4��ge8*�m��#3hשx6����v:�HbnŠ�U4,��A���"�ϧ�}�P�R�I����,9PCH\��Y�c�^;����S��GP߈Z�ͪ��-�D&�X�:��ҧ(����(�=N��ui!:�2�a�D��s5��ן�X�A5���(%��=��7B8x��99�/�Ìg�U"�#WTѤ&�n�L�K�-l9���#!�t��]�er�^^-�%���%�Ä3���:�� ���p�#D>���C=���T�A��r8�'An{2ṁ��+9��uB��v�].�ῂ�Z��Sl׃HS��$���+kjF�r T�{M��J&�ɸ�=�%QW���8�� 8f��`��Z�O �*�({�`z"���A�m�,�43iˈ����52g�c���lX���5\��r��8#3K7��a��b�$�,����Bpd#3=�Խo��TcxU���ީ�K��'���Q�.��xĄ�p]��#s�$V#W��g��T=ɼS>D���L��gLm��2�x'%���#3����E�<�'���x䣑�T���YL#Wj�r��� JI'/���,�I��s��ks� q��h�r�1�_��γ)��*�2��̙s33,m��U�fd��v��C� ٶ�߯d6�wa'�"��wk1E�ޚ�+��#o^�:a���Խz�w�xSgp;`/p�����YnI����J��k��gBa Bgr.��6��T|�>f�O�<qu�G�~I�dQ��P6�3���F�uaL)2դ@KkN>pbu���A�GL�ݙ�=ܖp4�Ӕe�D$D���3aeH�#�5���,�q��1*�]R���" ^%���{{�;� �P3�ȣ�Kچ�`!26�7�f�d�侜:k��.��=���Ŏ�o��g��U$ղ&X�x==a��rHx��4R��.ՠ:xZ�_� B�t������+����]S�-D'�ʇϰ߃�jỚ4@9j/%C��79��UC��8#T�K jB'��\�T��2T�߷a�4�m�1��@��#Wx�6Ya�N�� �����t7m@��5f��8�ޒɆB�b�[l��C]gz�m���HH�y�!�#�12�������y��h�lh���6ǖ\�X���|��ُ�`��{�!2qQ��U**�j^� �"hi2s��]"y��#3��6�#0�Q�w䱎 0�f�#W%�„,�����~#3�CclaQQؗ5(x&BRr$�Pd�z&ZOQ����"yJ��'K*L����^#��dh����:��`�N�غhU�l>5�P8aCSw�}Q�H�n�������BJ�o+��9t�:��С��2iH���Ԋ�Q�[�pI&�;�f3�9|���f������C�0�T��j�f�&�g�� w4�AvQ6��K&�TB�&����5��0��@�� �DR��J�;;h��)��6ш $�I N���ܧg���e4�#0�a�pT11Q�Ǧ����3I�!ܴ֪7#Wl�k+N���^КX�Oh�7�������΋�L�"�ފ|��^<c�^��Et�����Ә<I\��;$�|�?��|h�R�ch�l�1f����#%!��b��&�C���@f�x<Cbl=�btߑM���YZ '� �1Y<���0��a�O�I�f�-���Dh�u�c���u)ġpa[�y��rU(LE?D��˪������9��l�~P����N��L�r��b�łB�#<��{��wCfni����� 2$�)׾~��<���1îo���0FA�A��� C^�@�%������t���K#0����wMNI�G0��8�36��E��N��\0�8W���p/�z���0���LAm*v����j��^(u��s����șÃSr~����=9���w���m��X�����#W`U���R9�{v�������?2�i*�K��$������P��O�����j�P�)JԪ�g�����.={����b���ִ�l,Dl���չ\�e�;���2��{ڏjy��D���w�ҥ��B���R7�ڞ�(�Fӷg�N�<�QD��� p��$�I@@=�#�뜷��t�W�tR<|\��O�������Y����iZ#WIm��#0me�K/��@���~����V��u=�ԉE�6IF��1��wwz4��d6����X���D�r��>���#�1S[ۺ�5��̥۬)�t#3?;s H��("���x�����#3�#3�U�a�@���V1��K�Q�+=R�|WH�AXJ61~�����u$��`=�(�-@+�/�-1IG�ʟ���q��� "��(���) Z��#0y��۰:���������M<�i��Z;HM��Q7�u��*-@7�*#�{��z-�ʥ���p��3D�0c���V&��G@U~��i #0�!�?�$H)a�J���r�#3"y��#WMh�R*!�C�:̀|��7�#0�@:���>����?U�w퍶�n�TV�$�QkB1Jv����PE(PF+�J�3�bb\�#W��`)X� �`�J��T���* �@f�[�-����p��������c�@�l���'.�/�q�yUC�|n�����lF?��U'g�.� Ȅ/H�=F�XK*?u>�I�*|�Tp����B��#3�ս5�����_�Y#W�>ͫ�#3��`;s��.���q�E|�DU��#0|bE� �ޙ�����V6�Z*��ͥI6�H�8Ȁ��dP�A9j֍�K4���o{ծ�#3��b�./�0���I=V�c}��0Tk/�Ft,��B�p�5B�I��em�� [�@`��M#��#0� ݁�U#3 �Ub����e_�d����[�RI�wo�\�Sr�7�v&�$Cm�2���2�W�+��A�\�;���IY]�9/Z���Q� ȭ��R������g�����m+7���3d�)W������ #0�ȹ!p��%-�3,�g���"�#Wǰ=��s'� ����d�����J�\ﲈ_&��,�}~��{7��a�<�����(���a#�[�g{ k0H��+�vg.Ϣ���s��#W�X�A>SH?$F�3���ҹ����%�#��}x�r/���͂*j��AQ�#0H�Ȓ�Ѝ�ת�!$c"$B&���o�H���^�]�]���=� j�� ����9��Ov�g�/����~��g>���6LQ �f��e���p!Ly�H���fC&��n��"�#0�"Q*KM7P���)�!al��t���X� ��q��f�oJn�x9��I�<*At�'��l�׫��hTH#0�@A���q�t��OxXm���#�#0�ժ�8���ItE�:�׾�BY��ma�[��Ȋ�I�<�����4��y�x0���<� �%˪��L����2tI![7G���<��yy.zd�U�5�l�]�lg��c%�����*�U3�a*��#0�����J�U(����C��u�MPJ;#3���K��yܓQ�T:�#0�&�V4��yR�U_�Mo���5 F� ���"G)%|��{,�T�R�*(���S���!;h�1<�M ��D��ilDm��g�Uf�7�/U5��}ƫ�`g\4�Z�߫�=�T�PJH-#31.����BSL���U�B����AU#0��ZR�o�U�^ՈF��ڍU%j6��X�M�I���5��[J���V+L�f4��3*��ʶ��Ac��q�D��*NP��C�T�"y��A�F@������:�)J�ށ��X�a6� ^vDn�Z �6�'�˙#W#3#3���9��P���� 7�h!�#0Y�� &��%:d�g]�V{�D4��Q��B+Lj"��女D��1�Sn����f�J��C"8a`���Yh]�#0�2�rZ#0:9ν�q��=pP�E#0@ٵR�K@]��G�0<&z3~�5a��khbn��L�]�+�)1p��!R��f����=�Cϝ�PvJ��UR��M���H��(&�@,$;3� :��X��#3��k~ ��cM���\ۖܭ�2�K�����6ت9ȥ�Ͽ3�M`�C�d�K��Sy���#0�!�rTN�T6r/����o���?��Q��@��Bi:(HɆ��?��s ����^s�Ogr��`:���'��(#0��#W*{�GP�������Ͽ���*�`$����"I'�0��e� x�(��7vצ����x-�G¡"D�&�#W� aH]�D{j���m�/��:zyX�uD��:4�x ��*.��r��������k̨"����� y��L�h��dㆁ_��1Wp4r��f�x �v�>|�� �JA��v��M� ��Q#W�9���#WA���قx��ۨ�:A�$�S����L�ӵ}6��Fo{�9�\0�@�RL#W��v���')�p�#0Pd!� �.��o�����>�W�Gp]��$��`<Er@�H�I��#39��{t�˿^�CP�3�ֿ'S�2;C���P: �ht����88�����<��Q*vKι�]q����{D�C��X:�68V4~�{-�>'���C�Du�CL��~��O�q ρ�!��R����/F2���6�1�QЂ�γ�"&ytu#3��L #0�t=�� �a�F��P�3tB�e�p�gڅ ��}�Œ2��%���1���V�N�XR�X7��4ŕC�\��Y,���k���ݜ,�8�m �ˌ�S"�@$�O6��`D����33�����'`���"�0(��MV�C�>`�Q⠛#3ǧ���;��h�?#WR���N���o���!�"B����@ʈ��*��J5��K>������6?5]ݮ�(��n�"������L���=Y�N��C�|�t���Y���b����A�X7G�p�67$���:X���DwH04O�n;�L4Z��Xb�Y���K��k�^�r�cY�u�������h�@:O̶NńTt"@b͠���4�0�λK/r㓘��v�:�����w���/ �� -C�@;-��HU��\{l-�Nܮ������Y>X���}�f�9����p��ߝ�d+��tLM��B|���L-QX�ٛH�$��c�sN:ѹ>(#3�����*ێ������X��y\9�Q�ڳ 7��:٨m�fJl K�g;p�k���N�O9�:m�f����F#3��t�}E׷<{��&��H9h�ND��u�ɋ��g^Mgj#3�r �{z����7~��56�X�;Q��c�n�$[�8�)����pJ�Mc<H#�DQ��M�&"<�V�M��<M��X�������7�^8�]�𙣳y�,��ع=�������k!��T���E�wo z���"`�ՠ�Oa��`�NhϜD�T]OE�WX!�+˻G��V�{s!Mg��\��b/�>���}��.��� �Ӧ�؛h=#W���;�$"�F;��8`Ŷ~x!��S�����F���#3u;��u���]6#3����ъ��I�e@_�"��~�� ��������#3W#�a���eP�z��4��4wNq���ۓ�#0�5ɰ�`7��l���$���];8ק���aջ^��sZt��i��w9� ��s�����H;��8H���UR��"�!���v�O��������}w^K"0F;4�Y,�n�O�c���ǰ�EJ=�7�3�C �d�$`��+�����;�C&C�$`E�1$�~�@؂ �4����3��������^�͸>�%�`�#W:g���I�nΞ��`����;�H�O��G&jk�$�/fwb�g�r�r���SF��Գh��>���5ԑ'"wۥ��GN&=�d+�6;m�N�S8�&�aŐ?�ճh�FAm��ּ��/�t���ٓ][TZZtl���A9�ʶ{�U�,,��� ��)�,� �5��6D�uu.��E�bՔ��~_<x��RB ���#3���Q��XHUR`� IM i�kw"#��T����ecE#0�c�L�:7Q���$BH�F@�d�D @�TI#0��M��~ u��dp�v��>l^�0R��8oß��s,����~���u�J~?���"���+���k��o���W]��<��'}���"��x��/E{�����eQ����n�u�#0�G�U ����4:(,��1g}`�^�C&��!���p�Xs[)�pT5\�`�Q�k�>{�p�0�࢟�:�HM�q�#W���Ц݈y�o�k��٥e�1PM!���n5��V:]��M5tm<��ބS��������A̩ ��� ��֍k��;���ݤH�)l!B�#NF�a$&���cJAR��5ʆ�%�B@��N'���:����S��mk]|k���j1j s�ڣPR�A2�I8f���5Iy*�~M~<�z�r��ʏ�6�o"h4Agŧ����fg��y������r(S�g(��T�#��q�5�&ZZc�3m�;��3s�/���"�,X���W#3D����kɡ�s�ܝ�����1�w+j%�3�T=`�h�����7ͮ�u� ���ڼ�#3��!"�46:��0R�3#3��@ε�2U6;x|='gk����OSb(Ej��x5iL��U�#0�0���?2�#W�Oo� ����E,6��(F�^��k�#36�~��<ŒF���������b��s(�38��a���59�}CBe�NC�.~�a��~�n�� �#0������:�y�o{^�!i�#Wd�R3��Q�w;^{>��xE��r�\����G�n3N c0 � d㝱Y���?w���?ɮ0� Y���Rt�pbe$�i}���\}�`VdB�\� ��᠄��g�4)!!hl5�R�ղ�@��t>�) �F'PA�r�a`����pK5Q�z 5�:�/#W��� ���%�2'�9��^��هƈ���CP&$���v�e�z�V@@�����A�m�+���uט���k��Z�d(�`�Z����� ���J��G��ː�p��vT7 *�/��x�1��6�G�h0J"A�M�D�9kK�#W�X�SKD2��V�+���z��(�a�K�K ;�dy~�s��-��� D�}�y��E�%)�I#Ww�锐A�5C2�ư3rW�!��Oކm:�dzl)(��oJ�A-�9>�H��6�#0?@��L��d�Lps�>�(!����@�5m�����v��/�%���ЃÚ�7qّ��9&�$z`����-"K\b7�?d��AMog�?wo�����u2�Z�:u�!!��c)��G�.K���5^g�$0�gN�s�^W�v�(ӗSbT�-�ϭ�ȝd��(D�i6(�X�UT`ɷ��}��YDc5/IB�F�Ov�}��ܙ��^R����iv�����k#W���߈4{�f�J��d�'6���L�|#0�I�o���zS6S����;�9��jU0D)��e �M-���ޏ��7���#���ȔJF�E���R���!�!fk�t&R+1b(��%!��V�ƶ��������>DQU�)?��1F3���$I4@���H�X�#��t���#0vDL�Z�$�[�F�[��4�+a�,����&j��Y7#Wp�Ԁ��ZFtHF��~���`�����CLKy5;����1Yehxa5(��� <L�(,��j�n(�Aa~�#'^rq�Y �5%dej��c�,c��!F�bS:�B�#3��cξ����m���N2K>9�o �f�LlUȒ�R�����z SY2h#3P�a'��]߾��a�Al�D��Y��7�˭h����=�c ��dO�}pnf��3b�Z�&��ѧ#�b5���J�?-&#�CV�H���5c�������Fp����W�Of���3XR� �nŠf��T=�і�FN�'P��W�\��Z{9�y�SJ�����6B���I[׿^����3�}�E=�FS�Ɓ6m�rl�v6�bOo�F� sy@=�c)ڭ#3ZO���Y�7�9hc�-��tb���Uz8Uݺǃ���U/O�$�Xh����Nk��o� 5u�qJ�\x�<r�Eĝ��1W�Lk}�G]ᝑ��cz�x��sn��G�������Bv�q� ��`@\�n"���0Jw\�5-����J�O;�}-)����rWF�9-Ex(��*Ⱥ��#3#3���<j$8 &�8c#W��C�*o�9�f�� �#3V��RMD�D7a���j2 `���|���O�xt��6���'3����Zْ���""l�|�,j�ȅ2K3IU-#3����A�F,�*Aa��V�::D#��v�}�w���Q����RfyR���),�W���F0�#����਑ѝͣ���ԍ�q�Nm�G]�z�v�JN �V���~L1��qÐ�DJ� A!� ���5.p\�1�v9ٳ�5�A���S�L��۷Ü[9�C�SB�� �$���Q�D$E��@�F��b/�а�"tD�o3�=5�9^�m6���k#�e�����Hup���,pF6��BN�a=���"k�8ɕn�Dt&]Ns��1s��/ �AED�c��#W�#W�p�R�r�6� ���UV`���lدA1HE��~Rۋ� gӢO�2�07 mp�9���A�ߓ�,9��p��wpD34�l��ٳ&ww(1T�G7�LTa씳Bc��n��s`�!���*�؆:~}��/z�� 6 �)���g�˨���l5� ��J����`�c�#W�!��E��/��Lg��ғIP>m�#W������8)Q�a�#W:��$��I��4�#04*z�Q��������1���<�a��Sz�Bu�y�sYi5K�K6>5��4L#0-��#3�99i����`[L��P�Cאk��6J�G�:�Q�|[@�V,��qמE�#n�f��9�L�4>�������313�����(��7hJkV\}f�9�sP45�}\���y�#����)�K׸�c���K�YN ~�K3.++�szc�������0!$�=+#0S��O�X""#W��k�x���ڋV���Q���QkkFծ[nm�� (�(HP���瑨-vŒ��@��Vhe��FR��pt���D#0�L�|ϻ��#3� �kc��&���5�V��&Q�b�&Jf3T�������Q��i?iԔ![H@���j`d٢%#W-��{��#jLY%���ɉ�f@�cF�UbiD��{���4()�$���¨3&�A5Q�e0�0�1�ZQ��� L���)�2��,�S+�&l�H����pu:NT�!�#W�ʛ׊�*#���` �~pT>��Ţ^M�fc�w_/��V��?3An��Zb�tHL%Z������h����:CRju�0bo�}���ژ�CF6ɰ��p&�:�9uóx�y�Q$j3���b_��hCi��u��%�@3 ˶������v1Z�%3�?���fq��]�LV+�͑����7� �D�1��z�Ot([��\��i6�c)[���� �� �t����-�*>�Či��ȩ��b�3Y�`I,�G���}}��WHup�xU.2aDr��4��k#N��bʩ��� =z 0��0�]Y�a"Y#35 �׫�nf_Mp;r�;W�����*F�S�Q@������y���~��T���{�0��R��P,��4^ �#3�L�f�o}���4�:c��nX����nGž�ztu�u�U�@�t�(QL(�R<�g�I�����(�����_�����t�:EK���}Ͼ��ǂ"b8�ܶ��w��z��Ve��-kT�����#3q�ȺL��z��q�D�aG�e�۵8�{��|Yݬj ߲�\ʋb�Z9\��ٯz�B8f{RM M�v,o���"���o�X~M���,7���m�����ic�4X�<���o����>�81j d�u��ޏ<ί�����z��r^�'=8ySeR��N�{�+s^)thd���k�k ��~BXs��}σΩ�xIH�YQI���N���OdxO�{3��C��l�6��""#WT�y�N¹�uAA��5������P�l������}�© (��Z9ɂeð��8a����`;�&y#W���5�#0�=;5�F�ݞ<�9) ![��*�F�P����7�W]���#WX-�#3�/#W��{FLNd)&�� �W���I�I�Fp@8y(�|\��P���� t��Ȟ�q@�C�K~������Ft�D4��]�Qe�x>�8$Ώ�}��>0�W/�K����.���t�O�T���Q��M�t�8�{ ��C?J����UMU�2�׫X�$EMn�FP�t�ZfD�i��y����� ��#W<+��<�� &G��@"� � 8�R�i�#m��/˺�G#3 wo�u�!�U��s�j��Ռ���)fO��:5�Ƶ#o+.f^#3F6̚��� `�Q��s�F�5�#3���3��F�1��t�c�i�5#3~�Mdwt�O�&thE�/i����� �1��R<8s1ڲio5�D�dZ.�7�3#9��&��ՓP��m�k�o'��������Z0�9��з֦Ս�8�yzŮV`|��j�6�00���D�0A$�x���>����y�ƟOT��4c�#W� �)swa�f"Q�sx��-l��A���Μq��t���r�����y��;^�k��s�O� �H-�$쎴H�`?�Gjɞ����'�#v�F���$1���d�Q�Y<#3#���{ǿX�����@�!p����s�A�Hz7hF�g^���7Q$i��� u�ttn�*?s:e�*�V I|�#3�#35ux�i����F&N��Ő�!և�4��c��v�����d��v��;r�U\�6�@<R�iS+O�1��cI��>Zn6(�kw/d&]-��Sa�_�p�&4#W ����)2**\��Bm�M��R�V �5R�d��a&�|{(<�ͺ����X�bNQ�^q�L���=+Wx�W��c[m��_Iֳ�m��IQ������P���~�o��y��#W��Eӡ�����Nm�D'�t���Д�X����j�;�y�6Î $G~�^�{�;6֍�%3B�_{���ˎEo�ƹΥ�<� �^�P���:}������6N��oj�'�D�wO{5D��%B�QI���L�ƈH�y���`���%�e6�H�#��R��)d��7;,�#3�F�=#0�gv��f���"hq�-k��$��DF�iu� �AA�?ybEa�P�"Hu�&qT���H�I�#ut��NL#3+".��#W�WVK�ls��w/hmLI����ɖe`�����T(ߌ'uƬ�'��372W��.�Moy� ���9R�\��`N�԰��`8CA�w��I���y�JM��[=`�D�L�7<DJ�U[<�:E�Mj�ѧ�#3�!l�"�r�F�t�.#3��h�h��9,aJk�:7�9�3@ke#WMw�/�KI{��:n �P}-N�9�Tt�ډ+DZ!"�2�k*��k�]��X�Ӓ�h�W#W ��x�M��w�y����&2�3�L��ڡ�L,�iQ"�C����E<��OXr��sd(��UHE�#W�D:��R��= �9#W����S�5�#3Mu�!|C���'֚�a�g@p�ճ��ö6ؼ@����+ut[�\�1��u&��7�|p���^7i �˧? � n�̺5#mָy�n��T��� rl�y_��\��>��{Xkpk���O�W5m�i���Fos;:�`6qap�2v�F�|7���a�kFf14�y��1�������@��3:5�{~�Vc��q�4�l�m䔕<�EU!'BY��P�&��i=�0��I T��%� �[� �y�ʫ�z�g���Jb̿Rh�:TL�]US�M*��1�_�[���� �U0�W<�����Mv�vÊ�E��X�M mMG�d��aA��ͳVM\FJ�!7�֦{�փ ��4��4qq�St0x��$|8-<8� 5A��M�k33S�I&�[sn�2n�ᩘ̺u�3lvj�1<�d�t�.i�]����1C[$-�G���R)#3P���ÊN-�'#3wќ���OΩFz6-���� �9#V��(������,�5s�McEJ%6Cno�a���L�8�#31���m�c�.qe0��:s�"�#3�;Ճ���p�G0(Bs(��#33��[��$Ibh(�ѰR���H�j����QY��U�c�aɻ��7�35+�jX�h��T#��{�V��� a��tز�Vip�h�4cq)a��1{�0��s��d��k�Xs@���`�#3�c�#0r9��U#0�K� ��/e�&��l�E̶��b� ��������Y^���� �u�f�!���cr�a�������]�O@�<������ƃl=�]�}�F����.��z�P\�l����7sNb���#31��}�n�]S� �V~��Q?�\M4'��c ���3�d�Cɒ�al��*��Յ'���Ԧ��&�ц,r��X��!X��V����t�9h85բ�vk���>#3�8ɢl&P�d�EI��j l��JE�3�Z&p$�B�sR�����I C�n�際2S!����[ݜ�ZE�ha�J����fD��sf�jqL$��عdt�2�с�0�ʸ3v�f�k���ҍl�X�r�8Dt�Jm�R�sB���9�N&�5d₈�(ct]w�ު2���$teo��=9l˗���T��B�FM E��.+4W�av�;R����y"6��Bs�<>��m���۔,�pP��3q�^q�1�(�s��t9��96x��Ί�����K�(�Bv:;bN�ч��`�%��F��g1�5�#3��q�k��f��� �����hCn�Bl�#3�~)T��2r�R: ��#35_�&39q� D�A�����yOj�|"ǭ�����ƙ1^��Eò��� 7<4���H(r�=Fo;sJיP��`-�!L"�R�P�b �(��b�#[x�c�b��h Mx<C�j>�3��O Tl��d�#�jH�cg x�x��� �lu�k�mד���H�x���P�1�u��`�s=�#�� �m:�`,�A�N���Cs<�#3��6�`C�Cu.)l[吝�8��jr�y2C���fL��3���@�3L��Ա���J"� �(�5b$feeh�NTq��BȀ��y*8'��4_ h�G!�\7�/i�@T��(�D� ��lt�;���((��.fZCH���e�1|c�6lJډ��#0\��]0�Ѳ��(vgF�d3>�rB#3�A�(�ؠl#3��%C����2�`m�6�F�:&#3� ��� �f���)a��v�G-lq��0Ś���0ͻRHf�QZ����i2���#�O#W%V�\6�'ƴ�%�iT�!h�e�����鴛��񾫱��HED�M@�fw��.�Me�<�H���5��<���[Z����.�EX����B#0��To>���A��'!U`#����F���򍠒�; �]B�p*,Uc"0�2#0d#0ʐ����pZ|r�`���i`4�;�ڷ�n�7Uu��r�;H>��|�H}^���gu�>�����2���g7�)L֎1�w�� "ҕa�8֟m-��V����-�G9��ٳX`�;Uo� H��́��&�m��ǩ�:M.�HG! �ACsNFFY�i�/�z�^���Y匄47a�]���߳#0׉Ȩ�[`YtRZD��Dn�/P�$�;���a�7#3�m��ּm�ٷIӋ��O�ǝrU^�Q)�=���K�t�KE@(U.jT#W+"�B'(&�#3��E�+��#Wx��u�|��G9�<��e�D#W��F?�6|K�J�K��u�u�=��^ hhx#0Q�1���z�~����~{T�$=J濈�����vc[��˛����l�~�Լ����5o�*�k:��;�^=cȖEJ�s_�#�Sj��@w�p��0#0!�g�⇏.�=�W�NP�!ȀU�##0�"���@��]�h>���ɔ���bt�O��P�`��3���\���9%�5���W�ڥRb `0(�` e�#Wc�M���'�ne��=�PǸ-�͔��4 v#B�bP1oQ�C�`�Ѫ�YQ� �G��q�2�R�\r�ì)*ƕ�>�!���#3a�����r�ˆ�D�he�,dM@�=G��_#WN�#M�%&�-��F֋Q�1�ġ�f���B��E"���S�b��)����P�h�PR�jm�~����2^��v�Sa��Cг���FR�|�kK�|���)�ˮ�5U�tԉP*Q4HӔQ�1�gMd�'����IP�N�6-���)�?F(�C�?L"[��%��WE)� Cc'�zQk�������<ݥ"rLU��E��E���áe��ؠ�Y��x�g�ӞB�3 k��"!p�Y V��$y�}�>#W����ZH���B-&@�B��vL � i��%vL`l�R�#3�i��U��3D"܌� �|L�D͌pgܨEb���$|NԴes��fm2ɰ��y�[� W<f#3�|�1��2fۼ�d�&o]s�3�4��H�ň���߮�ع��A�9!R8f�J\�Z!Zꑴ��X�ft>�#W%��&��)�N�'D��?���4\f�n�Dgq��A��#0m��%ݢ�@�$f��9��x�/0w I��1��)� M�$���JAT�� �1,}a�K� �J}�$B�K6iB��X#3�P3@�@CC��5�$��>n���^��_&.��q#k-���m��L���8Q��Ur0�s{8+h��&E��Œ��L� ����;�ċg����6l�B��w;�^y���:�}���cL1���F�nȧ��������6�$��3��#W�Q3��")|,X�;��V��3#W��1Яcn���Sy����)8r39���ۋc����#3-�| �#W���p�0~}l��$�#�c���1���Æe�g3�,$BN�Ʒ��JS���t�jטW��"�F�E�mC�8˂˵p��4�W�o\3��磼\C��d"ju��,�,�]�0~Ȩ�a��sr�T42U,��SFQ�uv0�4GDSͻ[췪�2컴���.�gB��430 ��dս`%���*�� N�� ���8�0m8#WQ���P"�.�#0P��$G���`1�����T#3��_���1)�E��`�Q�#W�(�5��? @�"~��lWŽ����kc/��9�P#3� (��4C�o&3\�LM$�2���F��P0��UUF�S;�-�%��lmE��F�b�`�Im�~�R�Q�_�~}�����>z��=-�j�3�s2퟽��j�7�8�� ti]譩���MTo��R�R�����n�X+qm��5qCU��ȡ�o}t�����B(�¹�rIԸ��ڔ�l��ۃ#3�n�eo���k�J1E])DM#3�K�#{0�b�vʯN qPF�X\��g<25���0p��\;�����m����2�M9&y��!{`���3�!�� I��vF8|�"�D{tb�;��1��8�SA�'��t��q99��6p�;�I�'s�=�!<':�����cP^�P����n���I/]v���cV��붬�jw�`�#WG�y%�fN��V�t;FY��Q�Y�N�rI7ٞo^^;�N�o"�$����ҵ��r-!]��d�e�$���'ʡ�ڙl�X�j����Y�`ԆD�#W�3` Ț��_`��%����m��T�mͻ)�jiG)5�S��Q��U����E7.k�b��z�v짝�ݺ/ � ǁ�I#W#3�h�bHmQ���$�Elj 9����0`�I(�P[d��TOҩ��2��X9�#0��4�wu��SA��.ͺT.�1�q�++q�J���d���11��*�`0al�+Aji���cL�$a"9���<�w����7%#W�J�<��o٨�4ʛ������H��4Sh٨@�ԟ(�܂I��2���f��-eI����ֺj�6�5��l�j}z�v���e�� ��[i-�6� D��D��!r޿G���������(��^�lU���&A�*��g�K) H v%(P뀈�;S�Z@���w�C�w���<O9��k��a���!u4#3#3}�ߧ4O�(w#W�v��Hġ)���-�A`-9Y� � �v���4��5��[�t$"F#o짰� Ne��r�-2ي��o,jv���az�a�T�[��:���*UP�H��R#W�� #W�`��(�-#W���m��l*b]�cm�qC�,�tJ��u�T����žb���n��#W�bf�����G�6¤���L�c�� �s|�R@�.m!���W�׿�v8n�����#0�a��� fߏ,���𱓙�I��{��Q7�D�)�@��Aj���q�l���ܡ0��3p0�bA�hB�`�;Ch��b6���,��J�������%���L8\VG�ds9��r�(��Hj������.3�Ԃx��#W~�IKƏ<q���O��V�����&#3��>��"(�@��|ޯ��v���67��8A�-��׸���D���5���"�D#3?I$������m�KӲm�ZC�>ꊲI�릚3i/n��Q�x���"��Kd���R��,�[Ki6��i'�SL�<���2i��������k�)��a�٦4ijh�֦��'�m#U)r� }[\�n5����h��J Lڬ�m^�R�&�"-,ceW�6۶�nț�wl�dQkD�+UR$�m�Zܻih�e���MM���)�6�M�S#W��6��-�I�;m�V��xۢ�f�uu�re5i����'�y��&�+�j�k�e(�XH�xMrb-M����W������9����*�Lx��D���o�ې�ȁ|:'��}3�}{vb|����Ɲ�7����ѫ�X��L0����O� [�I�-4�V����{�I2��u����֌X��T��h��@2�� 7�Km O�t�KM�LҨ�� �#0B����3=�#0�#0ˆ�f��M[ݫWK[r�hM �"0i��A���eWwTlQi,[P�"��������:�SJv�Q׸��H*l+PP  m�͘�b�c"���U�BQ�I�SZ�m�2�Y*�Қ)R�}+r�-EeQ-��F�RB�#3��JYS2Ll31�6#W�”Jkf����I�-#B��Q���,��,Z��"��4�R��M��Ed�i*ILRU!�$Z�M��ͦ�!BJLX)2� �I�J�Z��ƨ�Y��&mI�E����ɓF��iM�Y��6կ�k]�ڤ�Vj�Y,�CIlȈ�*؋ T#0#W���T* e���Ȳ�AL1S־mnj��#3j-m""�`)�*Q"Hi���p�`zg}���`F�v6��F���-�uA���3v��CMVǎ���`0�Ió`��j&�`}���?Ԑ9#0�ݹ�-T8v��S�8�P�$��2�)o*y�b�HC�e�~�U�n@C�����#�=HH�n��d�F+��n�k�g]wb٠3mf�2��3�t�o��J7i(xr�J틮��q����,�a�O,[���<0@�T��#3�l��ha!���/P)��k�fJ_����`����w��Ь:=)+���T�*���>F�W7Q�k#3�N���@�@T����>��1��Hc�6Q�K�әG�H��rIlV~���<�2+��f��P���_]b��8�lq���&�Ce�v7)�diK(Ѐ���c�2�"�[#0�\�P�E#��8��X��,��@ �p������ <|h��5�=�G9R���T�ePT]jEKfL-�ա�u���+![8��jr�Y$gf�Z�8KSа� p��!�ޡ�m�:6�֎&E�gk�}|F��픐q�s�PյEi!�}atgY���́����ffZ�>��J�]���E��������ݧ5&#0B�p#0#0�6ض�����]�m�z�=�[����Qn�$ E *H�VC�a�a6Y({(xcF_��i��ֲQp��\��� "�"���j5�$�ޔ1Z�M�Ǝ#W��辒l���)��)���j�A��hJ~<�mǴPdbE<� ���{Yë�󗭱�][{�Lcؚ�R��P ���`}��W��3��μ�I.w���r��v7((/4nb��ם���^{<�u�,h���KE�B�#W_�ve���e�F6���H�-s@�Zx’L�m�X�x�jI�.m�Q`�sX�0�H(�b,h��#3u���R�nU% ��`UX�ÍA���v�g6�(��in1e�)b�-� a�U�D�ۤ��KV��E��ʦqQJC\R7=�&�#�#0#3#3�˪���9ލR�@�� ��M�gV!��� �N���6��#3���A���[��*�fʴkf��OD!Ƀ�Z���IR�=[��18��u79Vy�����j�h�'*>���yO9� ����X�X�[F�"6��PV76��-��֭�e!ۭ���y"C�ߦ|��,v#0-������8�?�:C���Ɠ����Ը��5!�#3���x$��+�[qi�u��l��$!��]Wɩo��ɮ2M�!G �f�!�@�d�#3 ��� ��%�!�%eL���۞0�HR5��_Z�b � �o����^��_�O����*�����~5#���L"J��Ԏ �����%�P��Qr!d)���:c�f�q�� ڂ�$-t�����Pۣ��׊bw_}Ov���*�8�7֍0� d!� 4�#3�MJJOV���+!��M@(X�#W4ɔ���m�����ۦ��֮�3�\U�NVZ x��X.����D�D���k��0����*dn�=�`�H2�Gu;>t]P��ˮ!q���&3���6\�t��;��AT;Ogó*T�B @�^-LJ�JF���L|�h:� 2@Hp�Բu,F�(��m(�t����i0*��6وxկHn���7�� +�\#3f��و�@V3S�s�! �|Q��" IH�\�[T��S��J+l]�����>�uY���|)��`�}h���#�#0Dl���ԉ0�����!�1�h(�;�#kd/ �ň9�K�}��0�q쁖sh�!�vY�El ���7L�[01�c@������5$��U��좕yz��������n���) Y�5<ǔ��F]��}z��DY~*1v��!�ߪ$��%���ɋ>jzR���;��'�P��G#0��� �Z(� ���G�߹�I��KC��9�JAB�RGJ#W�l#WU^���]�,Tda$dpP`����AlM����4�� �BF��&ӎ7����VB=Y�5[C���T�﹩1��(EUQTUPՋQ=�Z��%Rp�vԛ��M#3��X(�-qQ1�!$-����7�&뻗G��bP�%7tAVѵ���ŕ�J뺸�m��/�{z��k��geR�Tf�DeUL�Cye�ɋ>���P����xx����� ܷF���R��u��{�����n�LJN�>���p��s�I�a�n���H%��^!���j��� a7��2W�4� �C�:��\�;��2��>��tY��D������%T � ��uz���bW��)�N�#3:e!��{���ی�I���H� ��#$���.Z�J�;FeҊD���,��FER]SY~� �g{#W�f�_��H�X�0,��J32�p^`�H��ȒM��A�!�Ie%ZP[@�'�,L�Ha;�+�V�����T�ʩ%#0fP��H#0H��䴀��n���;S�q%�']����驫y���6�����LJ�q13�!:0�n��#04y�����t�0/#�@�p���׵��n���;�k�����,F�&ث'�V �f�3m�O���|}U�C�X�ѫ&�Y��P����G;�M`|�W������6��~�v�W��f�M+J����<SYF�ֿB��cb*�����(گe콤�h`����Vb���X#W� G�Ŏ��9�\5p&jC ��b��e>X��2뵙�L�-�,%�RTB���1�0m� Ԅ[@��RX��e���_5�1Y���dȅ& ��7�5ߟ�3_i8�5�� ��d�@l�C{���=�E&�!�� �4�#0�#W&�߶� |�oM�m�_Щ1����qR3*����WK#07{%G� �#r!�k����M����=�#W�D*WwI�i���m�h�U� �7{w���fC#3���ql�!���0B�F�ع�c@jc�%�V�&2:g�'^�\ȣ"!"Hv$dR��!]�S���{��pX[EFu�Qd�#3d���y����ޠ�=��3�4����8Ap��t��l �!h`@I���6�#W#3#0ZLK* s���٫�òύ�%�@Ϩ��o�U#2x��E�j�Ca �I�s�ױQ;�R��U�"&��#3����� ��#�`{�ZWj���)W�'s:a�*F‰s�^�n�T�{Bzn� �;���h�;�_�&�Rx�/����g� E�~g�I��0f B�+3`��=����<aAVk�3�ȷ���&�U�:�4#�:a �p9�5E6 ��@t�#W?8z/����B'��m}~N���kk�J�6�Uk�UE����sj�Z��^س#0�Z�� H,A��#0�I�����z �1xtU�,u�5+�b��Ib�NQ�g��ճ�D��9�3�c��S��#3�Ѱ.�>��bmO2zҎ���4G����#0���"v���E!*F�P��[�,���h����"���WA��P{� =�=�#��x�Q���˟��i'׀��k�>w�XZ �� �OI�:�������)��$Oy�Z���-��D�`�i�OM:��|��$z � �jň���0� �9�>�hh��(H��BLU5�Tb'�q,��� �)�$��#W�j���_<z�]oe��H� �FcAZ���Κ_����kA��#3�l|��:�����#W@))$F��NX�����(�t�}�Ii���Gy�u��v�#�"1,L��6L�Vu!��k�cF`���cB}@0�6�#�LLt�cAmVo*Z3N4Iz�w�ܫ���n��|��KPAI�2�D#3"֔�DE� s� i�%8��`�%�*;�h�) L)����Yh�RKH�h�@��S#3�m]:! � `MYi4r�M�+S-hn��Z@�:��5,oO 6ȫ#W�r�Z,M��*����9m_/]z��W�#�wt�k��r�m+�4TZ1�[��$�y���Cˮ���[�^q�4��nө�1�N� f1GƑZN��E���4� o��0��,P�(2ƅ#3�Q`h�T�\q��5@��k[S��0��7O\Z�M����5f3J��^7�#3;O@��'R@�F��#/��C�Ζ�$�T���kO��.�>��<��������T���:��:�ɹa(bĉCX%щ��’�@���A��1/NV�q��y�ûGD������`U��#3���}�h��E T�+���j����Y5�������Ȩ��� �em�jZl�Xҵ�j1�:�NvK��-�6+;��tյԬV�E��6�RI����!# XDu���H+��IIB�Nԇ� ��"ôPEHY1h�7���Ȱ��EF A�=v9x}~=���=c���#3�_g��rCCϨD1#0�R0IB���QP�0�Q@N��R(�ҋ\#gٹ�&��N�8�<v�zi=����y�����}Ss�qo�UX(a���m�A �RJM�z��m$m&��L�ڹ�6������Ӣ(�KVF���X۪��ZƑ�Lcc2#W&�4��@*�*qѣbRIV�Z�i !��@� `���Y���E##�:�w�64i����[{���q��ˤ�dBE#������eJ �$E��#0"%�B,��"�#3�@�P�'&0��Z���I$�p֠�6�$#��eO�ܣ�DH`F�z@�,�8�tu�Lw�ru�!,r\�Hn#�����w��K�#W��P�m��/#W@^6L��$Ѐ�MeUH�T��s���.Ϭ=p����w����i�)#W`�#3��lST�������1��7�T��i_��E��NA�>����#3\�z�)-a��N�]�v"t!�o1}�~�k�ή�vH�Y��5��[D�f��uQ�՚m����i]F�FQ�"�FIQ }�;#��9���였�F��lom�>MR�Eʳ������_.4�ʫ��Ɣ{���2f'�=-�ؓ�[�>��;��%�1�\����I� �D��?�n^c9�E�l3�Q�)�a�[��#3�p������F�% 0Ɛ#��Pb��4�o%vt���;� F��2�������y4�U�k{���X<<��<�g|H��I"BMH�(�#0OqG �����%o3��:s3��c��'-����dMw�Jr�Z-Tݖ0��� l ��O���9*l�� s���#0Σ���g4�~�#W(jGv$�>�l8�V��H��:�b5)�&����M���4i�� ��M�l��,� ��b(0h��L��.7���}I���#3[��[>�W��8?-��"o4�!5Ћ�4�e���Tk�� �ny\�٦~��Ϩ��[#�8l�`�LI��a`�d�1O��:���SШ�ؚ���k �>_#0�8$j<#0��\<�YD�EF5���ja�3f���j6��-M6$�#W3�5�ev! �������mnU+�ʦ��h�e5I5�ES#6ٶ�VZ6�S-��TЖ�R��V�D٪�MR�kj5�#-!#36{�}�[����I��C�td�6Ďڶz�0*���T�t~������A*(�"m���k\���8��*0�B%⤆��A���E��=GW5��UR��|�ܫ��[�Jx r�� �"A? ��E�q_V����T\�P�c*TK�G�#WD��(�������.�F) ���:n��.E�˶��$(�Ԩ��5����� #0N0@J"5#0Ogʀ;�#3G�9+�?[2=����)"UӉo��܋I�[j,m���Co%�W��/2��5^�Kd��J��&O�b��b���t?_Ƕ��n��&�b%�rY�������; H�}Ԗ����4��K��MP��.e�\�iz����p�_� ���+#WXXP�ȅp>w����D�D����9=�!=<���r�;� ��p�t��Pd;���e��4�h��?��sDC�;y���G�y#0 �Y?45 t9w�6A�oO#0���a��Д�KY1�rP<�:`p��hՄ���cj"�Bhb�6�&z6��7�9�%QEn,M�̭^g���6����0,�@��Ě2"M6�~;�g!I��_����~~����2ڍ�i�A� D�?�X�h�"H���1����% �"�" ���Y�v���z4��! �<沷�wo������&U�X�M���ՁՁ;��@S&�#W�Y b���% hQP� ��i��mn����Cz��b3)f� �bȰ�J/#34X�5�p$�6�Z8��*�����K6k�Z����$�� M4�P�؎!n[$)�B�tC,�V �ח���b�P�3偃Lo����� ���JZrᐴ�@$�� ֨x&H i]�Ƅ֭>W�v>8�"6^�8�g `��|�%�(S"�P���C�Z��:�� ����3)�D��L#04�B�B�v�Z�v@��Ty�g?~����%�8M�vα6u��46�2�I��Dp`�0n^1$� 4��ա/\7�on�lyo9i�&s#WbZ~х�w<O>��$"�E2c�.P��cDV0�K���M�-5/-]��-ۋ`����O���˾��\�b��v#0`��#W3��G����"w�Q H����b"��)��gWz�'����Lw 첥��#3x FM����[�NA�*=��F;wM}��S����f�(_!�\Z#3�"�I�dʊ#WɲU�^]�̯�\�#l��^���C��:2��#3�3є2K+�(Yd<l.8���㤯.S%!w�y�߅�#3�䉶��&������Y>�����U#WA@dC��c.�5,M�]�qK���T#W� أ4/�0���(m�6�8Ѥ�k��Mxʁ���sL�e�v6��ȱ�ܢ����Y�8�>4#WT����!�R��9��W�-`�2ç�,�f���(%w(U)�e`�H��ڿL8B�a �D�6�>�ⵧ�K*��4�<&�[-��Ix$�0��� I"�J�T�IE�RZ��T:Y]F=�t�#R��#��� ϩ�S^b=��$��D�fy�-|0�� ^�ЏMy�UL��膩�C�v/ '��F��-��8xw%��&�5���ŭ �d�jZ�_n� 4�ú���7�?YWtPRh�l&#W�B�#3i���q�Ŕ`�2�SB�� ��$��a�I��~�Sh����i��*p5��EV21*��`�B��R�F�^�s.��k��mM�r�dR:�`#W�.�U2#Wz�{�s��u���=����̸C��M�J_C�e��.��#W#�YN4�#W�իמ�7w#��#3ζ*#3}�� �AFA:�F��;8"C9h^�|���0 7,���e��@:����M��)���ϱ\�n;�̚���vۜM�E�>�C�\���qb�G�n��@���S��Φ*%�=�,�m2�x�<��ͼ^s��Ĭ�UI�ZKVZGs��N��˵דE�R� ?��q���ِ����:(x��hb�1-�#W������ �AX/ƪ���1 Q��� ϙV�[Ha���2�dDfj�&tɒ|Ӎ�=�M�w���A����.�c+�Z1�h#CTʙZ�#J��b�#i���Se� ַ I��(j�N��zF�ܖg��:�WW����Qu�X�Ξu6�7�b���&qB5�#�e1����֛M�dv�x��<bnj�#k (HF�K�0��#3`��&ӠH���H[ÆK#3��b�0-���4�k�( ��CF�و��f��_\@?mR'`qW?;�d&&�)Dv�H�#W? ���<9<K#3r� ?0pE]�I�X��o(�b= k��^=2�#3�M������o�(3N�LJ�wt�����:��"�V�sk̬�YQ���2ѱi5��-oԵ��ڒ��,�����[ ��P#Wd�)X � �������r~}M0,y�XJ.o�mSG��*#0�C��k5d*>�8��GY���#�,d#00+�O�} ��) �{�li�����󩶵�]ו���Ͷګ �LS᡿�tiv�hx�p�#39Ix��bj�&@���M��` ��[��W<|΍gM��{��)��t����"&�F���'�)1{m.8׺��9 ",��c�M/��XQ�y�#=U��ܼ��L;�f3#�2���6S^�0&?���M�^{#0ŴS-�`(#���_)w-��#3����*7�H_��^�m؍��p9��KU�d�o#�m�2É�����d�:w��ʓ�v�������>��Y�����#,�iEZ��y���|DvlA|Lj���i�v2n������=�yA�T|A�͵ �m9��XC�@&��屨��#3;k��MG������sdm|�7��5 �����D��;>ԋ'�B���7��=��Z1LG,������+�������'�{'����������2�(O�#W[e����^��wL��wk��N���6h>��uv�0�\�%�Nw������<5�Euݽ*��&ל�\3TOLP� ��"��M�R-��-Hi#3�:^�E�V�@�o ����'����7Y^��-#0�RX�4�>�n�b)�i0P�I��d�e���$ �6� �E��SH@� ��%E>$�$/@�F�n2��2�m5�;�"����r˙�N������ @����t��f-b�ڭs�N(x�����2�W��3fx�N..��kBz��T0*Y�L�Z� A�G��j`ȝ+����t0��ZǢ�:i�7rl8���]�/���ua��#W�ᮝڊ.h���j�hG#`,_�#3�"#WKת%Gh����̛��8��D(��UQ �'2��$�h�Ă���v�5M-�6��ڮ�؛j���u[+�����v��ַ�l�di��T�A�:=�5��':����D0����#WV�����6��L������-���k�`RM��#)79��4�Fn ����)��uKo��P���r��lj���x�%ח�9s�l�������#�+�D� �&�0����r繛���E,����4� i������*7��L�<y[y6�Q��@r�'zƁ����n@ɕ�{��)��]��c�1_�;��_[�O� �c���R�mr�u�O> ��qgj��?l�pJ�]n�ѿ�;��Vۚ��H��Pr�n�#?|��������l8MZ�2"&ѯK�Q�0}����?o�_ɿ}�Z?�Y�|�_\_�b�n��5������kXl wE>Ԗ�#WSz����ջ����$5)#0�m��� ���V #3¬�$̯Y�.Z��䍼�#3֒��B�b|��E�#��\]��q�<9FA� [3�vn�5�2U"��(�RH?��x�3���t����lP��Q��O�7!��:�sxH `w���ýV�~��Iy�h,x<sA�2ߍ�2�����q#E�\&�dkuC���a���0��3Q�`�i}TC����;@M��ho�b0���< �����o��2��,�+�a~z��X#3����.�L���1#W��ž6|t̀�忸?d�pb�]�7�F#0,��22��J��u�W�k�"ST��0x�#3��P�Y1�8ȁP�~��t4�#3p�0�<kq��ُ��� m�UmMpj9�!���M7�oxu���k;�X k�F #3�h8Ex� b'����a2�`�ԉّ�b�c�)��áIH�3�#3�{CĊ{��\�á�������="��>� #�k��P��2x��Â�#0!|��� #WtE#cb�:ݕ�m��/Ek2�Z��lj�j6���h��Qk%�6ɨ���^-U�Qmsm[���F�<#3�'a��w݌� 027eĤD ����F�!,*C�F���ça��m��~�3Nn �໗l�7��7N����`#WM��6���8)�7���]�X��'����L{#0u�WC�"2���-6� �$4VƘ�� W��d��E�Q�O`{��M&Ӻ5�?9@D�)'�$�~v�B�4���-����!���G���Ӭ�����L:���lGf�!�m���N d�A���.��R�b'P���9���BN*B#3�ˮsl��ս�6���5kf�o�mz� B`ٚ�àq�X����ju33(>�TM�J��~�*�J<���${����΋���L�(�M0�&P�tI?D��i�8&$��\�]�#3*(�u9�6����E#W6�֔��-5�c�W�c�&dI#3��Pc���!�1#$B�lPijk4Y=�����s��믻Y�m漘b��@��a#��`��� �I'�=���0_,]�*�j�?�ﲻ*d�Gqa9_3TF±�n�*#+|��1�=d(ᣴ3<�;*���۸������^E�@ ��~��r�b1AUG<]ԙ�Ӷ����[WMu�<>�b?W�/}����<9M�9��{����.�/9��Z-L�#37^��K�QY�Y�CWWD� L�$*�Q2>��5ͅ���4-<�ǥ܄$�*i�q�o@��@4���I��Ǜ[��)%YFm#3��@lZhC��G�e�� *0i��P�I��0��dA�CC��zh&��P��@`��n#3��=B��$�Z-M#W�D�%��1��H�A6�L �Q��4�T��J 1]�2W��C6#WOي�D������� �j5⊹_9]2,�6ԛ\�&3��i7N�#WRm5ػ�����D���£[(�L�K ���-$b Tm1��Ʃ�]D��D�!�i�l��MCB�F�5I!�+,��U3"�SR���˭r[���$&�[��\�.h����w�4hT� ��|�w�ٙ�`1���R�5�������h`JM�����A���q*B,`�5j�@���P���v;�j�>2i�m�EN.���I�Z���������!�#3�uU�a"�nL�6��d+ˉ'Ï � �EG6�D8���)~0\4��1�e�p^9���(����Y�`�B���z���#_�p�%nQ��f1�=8�It�U��󅲬�Ќe#�͔I�#W��"�ɉ2D��x\5d3�XX3vB�%�b��a��v�v�l3VU�I�c��b-�����h푕���U���z]����wY4��]�֘�7�.q�#Q����2�fRBK.f"W�Z�.�X�:��V��&&���M̘7�+F�֤2@�̏L�,F�&„�x�.%�Y��ݓsJXz@�3[������=#0�>��%�cOY�Y=hA��w�|�K$����0q�3{B4�i#v#30W#3�k1�l���B J����HW"4!�� �}��� #O�Z������g�����W�b�ا@��џ�yDD�N���5���ڣ<đ�\63mn��6<��{t���t{��$�H��m�9a]�W�q���F4��0n�2`p�c#0�6�.,��\^gҜqr���H�$öp�-6�ҡ��ETr؝(C�ަ�7�63���,Ҳ����δ��fh�"b��o������I֨HDC�0�ڃ'2�"����*#3��c(3*��l����Q�i�u�#3�M��* ��f"1�EF�[C�8�2#W �@����e���#0>�. � t���D&,CЅQ�ш�tD[F?{@sD5r:�S���A#3��==SK�K��3��K(���� ���.W��v�J�f��D��r��_?�^�іk�^�Q�w��h�+�+��1WJ*�x��DT��˨ʨ�N�h�Ģ��Q���kAc�6��5��IJ�d{�2%̧]r�+8�^�� �KR�#Wa�j�&�w<<4��iҬ��U��"q�_�/��1��Wu�*�x��^ cbI]7bI#W(6���Z��-��E~�z���a[���<���D:9FS�� �'y�>�#3��*��&�Igb����h�kx���F+!y�v��=��7�l@a�-�[�����0D9ߟtPh�F�V�!���{�<�8K����d2�d*h�N�x>Jc_@�~Qє/Wi^i�ㄢr��=��B�星}�����ʺ."h�e-��A!��+Zu�~�熐��W�������`t����({�KpیSb��f��}(���XHDd=?dǝ��d>q8� �@"�DR !�eV�p��inS.��n��B�XC�ADؖT��U��KգhF��uJ9"�����o'�׎��We�ª�Z�t�x�,Vů:�髵WM��2�j�Z)�KR+#*m�;����Z�T�5�mW��6���Z���[ABIl ��!�p��e�!멥� ���sK#Wj�W�v�We�����(Z ��0`EB�F�B��KDlW���B�t)�#e��S]�Z�B]"�����eD0� B���羓,5���].���^�j"�Z��Q���W�[�l�Z��Lҕ���6-�EE�Eh�-�&�4cL֓d�f�d�ښ,Ei��@͘ʵ5y�#W$z*A)a �#3�÷$�Fe�FBdM��[mz�#3&�D"�$��_y�e����_����[כ�b��^��(��%�N&��+�����(h[�G��*A�O�H�H�%2�F��o�z������ҫ��p��d�WWb Q�����cI�w[������#Qkh�CIZ5�Y("� ED�, �LFH�� �JU,h�l��!��wb����f!�(�'�U()A&"�@C �#W�c�CB�B�ۻ$=���F�Ծ�ź�^����ywl�0�Jex9M����50,�c�Xa�p��v~0���n}��=���k���+͂ƹ0{0ϕwQL 7#W��!���L�p$?�F͇�TFp֌hЂ�,�`�R��ut҈�n��i�LcL$.RR�G�a�&A�˓o�l8�-���k �K�����^�"�m�����!�Ɋ��g�A+Ixs`����� Ѳ����]�����xс�4)�%��2bKȞdqsߓkM��h��Z�9�k��J�k�X����\ц0�N�&N82˓#3"]��{�4#W�ᓺ�c,�d��Y:�on;�+3/��W���@Qަ���C��i�ܼ���>QL�بŭ!�e:�iU#/7�y���Et~�������D�XDHDE�A���d_kk]1��)��no�b��KP$�4 $DQd$T�#0�#W-�����vޮV�z���q��>�� 1-~Vo�*?(^޹{�!"�,"�J(���Q�)��#0��@2�#0���N�-NHQ�V�"\CÑ�F��p�^����2 ��s-��V������J�l�1���.y��]P"���@�"���Jae�S'�)�U�,1��$NL�^ Gcah�&A�!i�6��ֵh�# �*�"�*� ��cbbaB�(��!B�B�Xň/����XafK7>򀤵`THG"ꁨ���c�u�u�#0����R0�yR@3��n.��P����K>����O���a`�s�>��BUxY�f��6`�۬�=pA�1�ƲZ��t�o�v����Az* ���$UTE0!$9�?��a�#�UzG�ѝ�2<����-�xxh��8Қ>�Q���ʉ�?�A$#&����m#}BF�>�,d��Ѿ��ɧ�V��� Dy>|O�WZϓ�~j Y�x����Vv:m����w e��Bm��1���BAp<b���Y��h��\���8d9pI#3t�*$���u5��zٓ�����$���K8���vZ�e�*�b�[���k8(äR�؋�!�) �ER@#W�$x�##3IBd�]M��Q��L@7@�p��\د�G���� �[a�lz�@�p�[}�=S��{��{��Eʓz�]���c�{�3�M'� I u�j�J1j��[����P���g����P�E�!����vE�nt\#J UKR*1�B~��d'C���O�ci�g��<�N��)sj�iG]�/!�)F^W%�,,��dl&ʕ��W54�M))60^7������nWq�7�˷uژ�D�]ݺEwm��ny6�)���n�]�fX��ʬ�ё�2�5+;�6�U�m��l����M���%I���t�3:[�)�ݺj�.v�Ʊ�����)�@�+h#DU��,�e�U]�#0�������$�#WEA�ǣ�4�"zð�D,C�����;�x��b5A@�J���F�E�؉⨎��ע���g�UN����Y#W�| ��^�L{�!=A�5������2CմCϷ�R}�(P~_w���Kٸ�_r]⤈�U����}:�������$Q3R� ��S�#0F �N�g���H�� �@�I#�"<To���:�k�˴c%sk�:�-]�s��h�C.V�Z�o$ߓ��m4�i�D3��� aĈ+@����_z[nmҞu��bA��%$͈���mm�_:(A�fF�8���hc$(׍�Zדku�ݶl�6򧴺j��m�2$[`r���j:g�n\ �R�T���}C�K��lP�8�B�"�`[@� ]����~����R�C��6���]����ll���l��@DU# #0 � �@��?�ԃ��"*�9��D�X�$H�a "��C��C��y0(kPH�>]8"TI#W%A#o�Z��FkCfklԽ��{�@ ��*Qj��LeLw 邒4KAM�(#0Ԯ��?�~'�\j�U*�pl,m�q��v='O��|=�ٶ��#Wz�K�'B�3<�U����~�r��t�� ]&L�(`�%5�0v�� h5HO�.� LGO�66����'�p�㣓]t.T�FO�VsI�L�]1n��I�`��d��z#0ZI��?��S䅷C@R���#0AIƫ$��1������n�nگ~��1�$��Q���H��#W����F�`#3��tJ�ֵ!*!4�XA�c1�[�PcCo1йhY%����M> ئ) �2���+b+ B��J>�"I$��!�\�B#WÕ_{}�;�G9�� ��I��� T��#0�#3\��� ��Y%z�Z�5��Fiw��g#0)� )�X39�LܹbZA����5#P$r��i� �5�sM2�@6���#3�p�4M ��=0������M� B��~Y�����4�#06�����b��ݠ1Q��D��-P�Б/ h^�wy��H���S�CF�aA�H�@� A9+}�R�sl*�in����J�tU? @r�$�D�H\IKj"كdj �md��t˻��� #5%Ρ�=Q7�i���ɘAb�YO��6�z@>�Ȗn�Z�6A�R�1m���e��?�l7���� ����k?>\_��,��5i�m]���Kjd�|.��9�eɶ�%�P�� $jmW�G�Q�+m�!+�����Y8fٵ�"� ���H�|��l�5P�/#W�I�+X�E�}���x���L#�!�>���Cϭ�6���A�A?N��q؍��6m���*:�����Y�)�G�EAE1�`�#WO]#31'�Z@���"L�<)���wq�hos���!���/H�t �;&���U��r����ܗ�>��:����~��JI��� �I|Q>PP!6�z�������~� �`������By�ٗT���+��9]*PW�*"��=������f��,� ۥ�>��ѡ�1����t��5z0�#0�Hh�Tw�)�he��-?��i-�ؔj_E;DL�D�#0�N �8c)� 1���8�G��hX�����ݙBV�����0��Z)�]�j}V\6�C�K\$Dt���#W0#0�AQ��6D���Y��;D{ӽD8#0��:?C�c�5uz*Uo����#0�)p|�� ��%�(7@ǽ�l*I�G�A�yy|Mv��'\m$~OOx�ޯ��"G�wǝ�e��k/�c�N[���X>�-#W���#�U7c��w)�[��g��(�#���[��;7�"�6��J���t��4=K��G��3�}{�T�M��蛻��]�Zc�c�8E#�V���1�)�\@,��W50Ff`��a�c�nM���~ÆE��A�s���d���qWN��o��+�❣9Ƣ2�d�֊Qsz�-;YҞi{��z�y�0 y:s�R��l�;��c��`��ډ�G?��^]�N�-�����aNP4qOj�qw�+��&��M�^Tc����ؗ#3���0iivk�$�Rݴ;��)��#W�b_��l��!��^O\��ץ��=��$w�N��?@�u�X�Dh�t°!Čht(!�*X`8Okr�B]��d�p&}���=��O��!���U�m?�3�^���3.8zT�5T �ϧ�v��#W����]h\����G�G��5�t'i�_M�#ra#3� j����bW�#0Q�}7�Z61*#3|2��!����%��#W"�c����0�������TJ� �REC-h8<�:�tv�&�6t&+yɲ�v���b��F�e��0�x��f�B>H���V�*�Z�UY���ZL�,������ �p�m��ɶ�sW;�ߍ|<�+ler���]��]�2������Edۭ�D�0���#3��>z�q4��[�#p�?�T�r����1ឝ����3<᰸k��'��Pv/�����׏,�P��g������dz�Ca3��ZH�EoA���B���hZ#W�LH!s �v��;[`X���銬�9� cd�J4���(Ҁ�;�:���{iX֪2��%y��9N#W�Lɹ��Zd��8��G>��2w떛�y[5�!`X ޷#0�>�b��I��o#3G�F,����nO� p��g����]2��%連'����K��l �a��?���Hy����-��#3�H��|�"^"22=�� �!�@�S��|�������chѫi��o��/]�h �X*G�����2����p:�&�81� ���]ӣ�z|��2�����@W�j90�[�x�ȷne��(�zNޱ_�� wM�9Mf��#0�U�y�R�@2��Eg�#0{��ݤ�w�+��T�<5m[|� �1g4�Ey#W,:�9(|#0;�T�� `�yB��M��k��B��.G�&:z#0D�r#W:6�#Wu�#�"�f��ΎP赚(���e�D���10e�i��9�v�O��˼LA�A@Q�B!H�5�3U����fy��<3�j;�f�P6}����U��>f�;oh�(4����bp�ѳGu8Z��x���>���qn QBD}t���v��R�*A�����#3"}���1d�D��DM! ��5$]���B`�@yv�<�'G��l6���#�%�G�#W*f~#Wf#WNb��O�iI�Oe �D��6Ɔ�Pn�#�k���=�n�.��C���v� ������H�x��������� )���^��r�w�h�i~���d��:v>�8L�~�XI�Q4���M�΃�p2�$ ^J1��������1��� ����_��[�r�_�^�ʭ��xm �;�����g�w���*����w��耵 ~]bS��Q�7�eU8��b߿\#0�<���)�oTSU����4L ��0��g{;z��݁JH;< ?��U�Ƨ���A? ��#37�!�"$$J�B!g�:r��m�FH"�6�B�]�Ī���#Wn�<1rH(���+�;S�MY~��%FA*5�Jx�i�m?#WБl(�J�cLZB����u֨ƱUKf�4��) �ۿm���I$`އ5�b�۝�>AZ��#W�i"��PA����^�>�TJ�e&h��0��(&��b��$Ō�SddؚF��Sd�P���n��<�L�&\O��d[HHj��A�7���?dR�M�v̠��0�h4L�2�iEH�4�ͼn�l��K�pD�#0�@|S|L[�U�r���oB�G^#34�C�K� Sd9EE�^�8ЪԢni�b]�d<��oGTa�"P��!��bcm�3��ޗ ��`-��MUNB7TRzk9>�f�!�w\r���6���hz��a5zl�C�@e�^Ʊ�p���ה�ќ��A/ο`bQ��Ô�J��&K ��C8��_և ���\�{�ąA�G��H���@�k-�T�JU[i�8Ҏ�0MQ��6���"fM�$�eI����sU�gu�J��v�����]#��;�V��k�׷���6��-S#WEa��� �c0��� `)�C1C�TC�ۢG�2D�h��cV���ش��6��Pdbr#W3LH#0��_��O����-1g��4D�V�,A| KC#39A���"~�D��e���s4԰���0���E�p����f�b�.���30!�V@�Q�4�g��:��F��XoE+��03J­\T#��wkf�i�U�=�l٢���j!%$H��&���6"����5A���b(�+iW��ZDN��[��ݮ����{�#3��Z$�m�V p�}l�l�"�EA1�i�A8�!�-�4ښq4�i� ,��V�1�#3�h��j�� c*���Pj��MʫC�%�;0���� L���Bh�Q�"�Ȅ�W#���c�؛+������o!)F�P����X1SP46#3-CW1/`��L:{�R J�E�`�E\�Z�ۦ#3A�c��OOy��X�4��4�"%"��w��#3���&��\|��:7A;e)ZEi��FYB�H�2"ReX�cp$���֞�Ao���q�`GZ�?E�bȗ#"CM#3��+bO��-p��聽CTӭ�Y�"�@��c#3��=. �A�6<��#W�x�5 A�1�R�O��%� 8G�A�^&��U���%K�#W� w�+(�)�ѣ�kI�`hdCI�n�DP�cCQ ����d��$��Α3�CH�%��# ��@��sȱ�v��ƤcQ���M��M��4Hk%`FF���u�L�L+"����̈́��XcF���FX�Q�2A5b�#W�@�A(4�h���Ɗ����i�8-��P�6��6��Nr�&4�h"LJ�DLb`c��7�Q���M����٩C���0ݕJ.��N0�m���#Wc+�Y�F�@a��LD�4R("��2�����Ht`L���=�kl�I_3�a�����.���Zoq,(W��pc|�#0i+o�L��.�1�6�Qc�4�;X%�����-��c��41x4����."&���jc�����`ѰEQ$D��4D��)�" KA�Bb\�c 2���*�X�%�Ye�m��#EI�BF����Ԡ 1�� � MbP���K#�8>��w�5�;�7w^ �_;˦5FM������u��r;��-�_ |�������(Z�E# #���#��V ��C��)O҇�d�l���0C4)؎�T򟛆:�/Z�J#WP{�yH.wd��?�<[��%E~(G\'��,l�d�����o�5�6�J�֚f��#W�j#0�.[f��r�'��%D8���� �9�����T#WIP�/��ws��&��(�k\̑� 4J�_���$Ge��ř#��wv�E�����{k��t#W 3�CEPl2�؝qEZ�iT$5��6b�b$��#W�\�;�滳G,�6o���`1�gڇ(l�_��� #3������g�l[��53�e�|w*�:#p�m�y�i�8�,2l�5X��@)#0���n��k%�R;t��GϛnGݫ�D�J"V������D�[Id�~�x�*�\����<���M��z��x����u]6��=�mP�,/T1%�nU^7��u�m�鮳�����{#3�O�}'�Y�Ӛ�ݸz� $�G�zw��#W�<��?��z�� �uI'�g�����GV$4#W��@�/&'i��U�������c;�0�����oUgx|OI1Dw\Ϛ��#3E�AB�}iئNz55����]��!ƨ�imH��5�K�1=s��,I����#W����m#3u��#W'G�X�J�^v�# �KE��y�����=�V�+RDUOc�H��Ӷ��Q#3��v��a��czph I�#3�x5�#��# K���~ֳ���H���L�ص"��#�8`6��c����Q"+.��WH��q�IE�s[%�[�5nķ7wkW5��Th�s[���5d5��]-�VMsrѭ\����#FԚ��wF����!6�`b�6ø{ݱ�v�agx��+�1��Ҍ�ӆ�1�3�im zN:�<ɞ�z��N]ɀ�D8r1H� �ELA_���"!� �}/ƌ2H��#W�"RS#0���wDC�����y<#W��}Ph��Z>"��@>�#Wj`� #3��X�)����4��F�#32K�������L)�[�K]�ۮ�;]%g]�W��.�R��B1@�A���6�Fڒ�,ڶ�[hصڨ���T�ɯ���z�.��R�%��ED7ł��F֖j6�&�F��J�F�8x�0Q ��A�G�‚z�_����2�Z��C#0��/L4�>TTa��iN.�*�ǩ���G��A�ۛ<s~���,��I�i�������w��{�����:�+������ �#W��R�uE�VAM S�A(P�@�#0#:JBY���-,�e�I�f����e)2JI�hSmh��mc[Q[SiZeR��SlE$Yd��_�Dz�֪�(rJ�2#W�i6�Ƃ#3��$9�� ���m��*�BH1��A���z`D��T݈��Q��B� 4D�c1�2�B`#3DV&����2#yH��d�M�$ƃ F l A�"h(�b�"�ʈ� �T!@��v�_ �Tj R ?��u��L�jB���B��Swt��\;S�j�7��W����($�$��m2�������.����!>V~�ӺIe??��>��疊)TR��}!*H$$=�{p�����{T�#WM�`�'.�7��·wO��.Xb<�K�.ڗ4����e�#0�0#@����%R�9P`�zqS�bf=1)eu��a#W��h�1�F#0��m?���_~��V��:B���S;�w�]�]�����]J�)�#0_��E�sQ��h�[[F�r�ˠպ;�R�[����<��0��4I���VIf*{1T��D2�#W’���Փ0]�3�)���h.�M ֣��%���i�N͕t�kk��f}��pr�`�� �ȥ/�����ͦ�W��T:���մ�J��Tk.��a� �o�[����-O�v�X��Q�u��k�k�B~:��o���B�D��Z��)�(Iٴ/�c�Ã���m��8��|q#3�>� �?�:x@�b���D $�%�j�<�:���T9>}g��Y�;R'n�gq�T������[=��^*HU���"�ѐ4m� �a��m��GY*�CԳf%� ���۳3Ai�yw;��|k>��|b�0�A���y'둙��៓�����G���B'�=��m�����8+l\M�U��阞p@$t�it�a�5�I��ljS#W��TT�H�k\�Z�$LJ�O�p u��G;� ��[�h� d����{M-�I��{j�#3'_]�l�]JJ��S��T*)���#0t�-E�����P�..��?ُY9#WG)���9+����=<�nmw^� �uڵ-G� {���� "i4���0��T]�+ٽ]�(/6+�уA��p��)�إ���Nf����a���H/�l��팅ˤ�8�]�2�G�n����K7���m86��m�=�Υ�)�\�"�ԋ��ſp�k�\#�� �;v�릳�ht��3�3�� FBe�j�t���-V�pԘ�@��򥡔��^���l�x��.m��"7�J�����-��L$��fb6߈f�8k��[������ u]��Xx��6���qj#Y�EU*�E� �<��Uf�tY�xo��2�Y��!�8��#�j�%�6X;noCf̯���� �%4��'�d� j��ܬ�w��3��r�θ �(2z��G��6�aEf�a�j}���!���u�'v�x����j=v��x���S�S�$�x34��sD�i�/I^ٯb�C�u�,�u���^�����!m���9S�͔�5�(-K���xzб2(=��M��;FSR�Zr����y�L�u��t}�EX,�sx�1f9���MVo��j�i�1��8*�+�pl���r��J�g �ט��10/(��87Ƹ�8ׂ���rg��V���pt�>�&��9&�ޟ�e�v})!�� xz�򪻬_7��[7D�K�Mގ Rc�s��"���#<=˾��B;&�wM&^0��5��~jU��˓���X�:]zLc�/����Z5���l"Y�a݈������'x��K�yȞ3��.Ec�bww*�3=5㉾���b����E�3Bdpb��7��oQi7]�%ޘz��O�]�6�#W �f��[�X�~/rs� 3%�R\ ��2O��������뢣p|�w�P�x�҅q���b�A�tT(��PC��Qa�B���ti��C�.�n��f3�w#t7��Aq�,b�C �P�N�,��)Q�MFoaCÅ6Jhzg!�I�x+��2)��[.�{�[݆�D�v�o����Pm�ڀ�� ���;l1E.=~��#3�J��A�0�;:wt��]�ŬW ��o'^;����)�K�D�� �@n��S��cϷ�=畎u���!��y��ն�OB� �=�����z#W#W^�I`�J�L�l#0�#WF ���^��s�ث�_���ߦYn��#��5K7H+#W�mGNY��ʔWN�~�Z��<�D��l�e�1�yL�jَ�&VQ�h�t�f^��&{�3l�i&�tQ�j��L�H�٪�d�Δ/#W��#�7wl{��� 7�o<8C����bLC�gg�%e���űe��&N�D¾T��"W�ѯ?m� 8E��g%f�V�R��Q� 27T��.�#W�#8�Qw���]>ʏ{��.?h�QB(��J"�ji��|3q:�9� �9Į�oM9@�m6;��%�nY5�E�xEa$�X|���E)^<#W 1#�uX�Y<l�hq���L6]�:kӞ*���x`/F�\"��l�S�x��,���:��}/��6#��v؝��3�Z}�t�h��xwu��xt���a?�.pn1�YVY��u�ԥ!�9d'���X���w��T��y���O�`��O[�W ����6-�q:8���e#WV5�v��`|EvR�L�tַ�5߮!Wf坤8������6X�w�{~< *�.v��覲�u��� �묷�SS�m�p.�f驸�9����P胹���գ�vG��n�L���ny����b_�j6���͹�)t��-�Ͷ4�N��_���t.:(���D.!/�lxbKrS�C���V4�I#WV�S�"A�2���A+B��la*�������G�{rh@�Jj]4�`bJ�δ���.�kEX�<ݛ �0a�H����FN�;��VU���>��Y� �"5#3 �,[�7,�h��tޔe�<ěá���'�w� ��x ��6t�����wb�i��*��1��-�!�*��Y�b��(C�{����`C;�l\��l.�6� 3��e����6n0�+��@�Qc�snז�o֖ ��4G����dz�l�F��#3M���-�na��X^F�O}��Y��Wx� M�S&n(3pR;@�zTW�����NU���� N��<03��v)�" ��9�̯��6_z��55N���$�%h�>��Oƞ�)��n�Vf�\Sڶ�<,GHAn��#3����e�<�� oa�����:��A'��^w��C4q[�1�UT4����-�)1(����'�y;`k����s��U���_�5s��#3v�8�="tu�5O4M���;�%��~����¦�e±Q�~� F�O�a��n�v�=�4=6uIƴ8��6[�f�T!!aD�2H�@�a3�ͽ5����)6�sk��N�d��Qx�z�k���9�� #0��P�)$,HE 2#W7��"�Z�8(,I#Wn�ڙck"(���X�k�1�aAT2*"#W���J$T�})���z�o�Q��q���/$��웿���8˶��'Y ���t�#W*{)�V��ⷍ\�M�imk_�mU�[W6���Mʯ���]R�I�5O��8�S&�%���2+>qr�5fYjt���U�cCK+ud���Œ �M�ms�3p5��4��ًD��(����/�h*|Y��b�Zۍ,Y�g���)K�{^CA,�T�)!H1WA�� �PDi��O������o�"D�L��(S�7CR@m�6C#W&���XԑK���lM4ƈa#0��EG�(2��ɷ�#O�HU���e�>ㅦ�Tm�E9Z�T�T��bC,��f���n����3R�X��Q�cu�e���i���9͆�o#�γF�уM��U�T�UT�N9)&��.��*��e��o,��&�I l�T\X&��$E�J#3��ËF�i��� M����[l2L�Z�wƵ:�'v�i����g)�%gnH#�#3�cB�V�T[��(@�#M#�.�Kmf��� ��Ii�B$9�4��\��f��+XI��njn.�3�n�ot��4�;�ͥ"N�8�7N8����w� *Lᑊr��$�Ί���d֩U2BgÌG*9�R��44���ZnD�E�nJD�B�2#3��4��M�MB�V���[y2X�޹�f��n]>4��#3�R����Ɛ��U��MFe���`���Tc�|f�Q�f�Ơ����&��ۈLwN�@�uXP��h����9Yb�?Y/*��>n:u��L Vi���æc�C��f���s뜃U��H�NGŘӎI5���(�@�6��`y,u����lf5Ʉ�fsmQ73O1��0ͮ%�JPH�\�#WF�M�ۭ"h�Em�T���{AZ��� a�*Q�b,`cJ�ȩ�Q��t�Eψi�`n �|�W�Zhx���"p��Ņ�)L8�� 4֬�oLq'يX��B1��<����?�gC�5#W�pm�5I�EB̜�s2�5�Ţ�si#W�XÍcl�,H���3����bLd#3 �F��Y�k,�.D(@�`�dYr �&�=Ͳٷ�`$�����}���j�20D��J;K.� &��ɞ��n����J�15�M��ڿe&�#��u�JFU#�&ߑ�(�kԀy�>�H$?i�#3�w���U���6�*[�ǔl-����}�b��ۗR��.O���s{z��jh�m����8�M�'�� $���^�N��AP�>������Tr�qRR#WC8}~�� �x���8�8���kd����U���݅#B��n�t��\A���_�ϫCUDT��-�˔�>���=��cxH��ԃ�1�DDNN�ٳws��m��-��y|�Ü��'y>M0;O@@�lÝÖ���H���H�C?���>_g�f3��z��*Y ��szP�����nF׋W6�*�Y+���*�T�m���cj����X�wj���js�%x�m��@U"�"��_���O�D�|?��~��޽}��DcU$�#We���Ɛ����(�2�i(Z�jƄ�6ɰUl��UVJ0�6T[���d�Rl��̢*#CZ#W�LKFh�4YM"�J0�I��~U��"iBI���u�[I�����<���`�Σz ����w3���l��>����u� tBA�<8��W����⫠��I<���i^��{qPP6���0/Z��Y.#W��� �|�o���^�l��JӉ#0� ��S�}�~ȼ�Ba�E��s$#W$��׷r�qS���ح��Dr�! f'��m��H]����=>�,�G߫�w<�c�Q�j@�iN���#W�(] ��3zMLab�#0�����[�Z�TIUb�DmI�����k��ﻭʺ�&�F6��3���c��\�D�G֖-$X��8 �x��P�"1)�*x6>^9X��e'�+J��� hLz��&"<�KjوÉ�r��m�(r���Pꢎ ��5)U���#3�9���<��{v�ȯ)TU��#W�#3�x�HƂ�`0 ���c7����gg+��ח;F2�і���%�r�6�� �7��2�lZ����.���@,�(����Ei��Ek�B�#WmJTG��uQ�!3����z#3 I�8uaLl�8d u1p+j�V\�U��mu�=iܦѷɭQQDcB��!�R�0]~��De�����g�����R'GX;�ʓ���I�B(v��{�"{Pԛ�#0\:���R#W��Y�T#W�214�� 5w R���rq�2�H�I$NW�b����#0�PM4LiJ�tKMk #0�=���?�qߑ�B�_�#0�wwt8�~��jO4��:oC�D�d1�3�ّL�ݤg�׿�K3����m�I��r~G0�I�&�,���`����F��O�V��Y0 Lm� �Aopk��A���u�ī��$d��h3�実�p���M˼c&+??,eQF���S%U���x-�S�}ADvߘ�b4���^' m'�Xs�����hL���#��(z�IA�4�l�6�m(��n���#3�ɕ#3�)@��#W5*-ب���s]u�V��-V�_W���5D�j3��o�A�}���V`f��lz��4�͕cW���D�R��V]�[��23u ��D$�Q��s�Xwi�i<e�����}D��hE5f�wN�T���xB�F4����r�>�����G��c�7*ZX!2�(��h�1�4���lѠTw�|>�$���X���x� 'p�B��#�1#3݃�T3& ?#3�Z7Ot#0�xs0?�t�$"��@DDb`��B���E��̾���o�����tx�l�����ن�Vf�#3�⠜��+��0���`C$0�[��^�#0��zJ�8<��"���W�����3�ϟ��x��?�M���_�B#�ů����A.f%~�^-.}u��wr�x�|�D��RnT��x� T�X��#:d,#[ܱ��JD6�,�{�jđûô˜��UF�JS�rb�@�3<->�����Y#3#3��3 5� �R�#0�"�dE!��{�R�#04@���U��dB�� TTZ'Ya�G�h�]ɬѾ�8�i44�i ��24yf��"�I#0Ļ����`�æ*0l|(C<�^� C\l�9���Q �ʹ�TI�{Eώ�`�K7 �<,f6@d�#0N���C�%��Ҟ�u{L%;�3�;c#W�:�ޫ^��`����$�MyQ�$Ea�J�A��?���1@��3R h;#3u�B6h"Ы�D���ml� 3��W n�mi��#_x�j#3 1�RESp�8\赧:�!�cb��x���v��qB�J��hi!�̼�����f�bA#W���F�%3� �C�y��{8g3��u�0h��9;Pϭ\;I��R��\f�g�'T3Y�9qaus8aī��ؼ~?�����mmS2��e39�s����ÂL��݅&8Y3^}�~I`�&�#0�\�Pn�lJVQ ``7�YHW6�A�����i#3#3� ��9�G��#�H^��%d�$�bJ#3�$-1mz [wFP-�yiR�$�ù*T Q�j�usGn+/ӄ��1�ec��Q�I����=J�27�1�V����]�Wnp���{�� �5xe�w|��(�1�8qfӽ��kfC�#3��X>,�}"Ki�a��a��3�>��KK{�;��I�0��m,��R=O:�1����wieQ�3�0Bϖ�2d���8t��j^c�����9�T�����6�&��-�m�YM.�0�R�f-b��Q��"ɳ#��f��q_n�Sn<[7q���]�k����1���y��� �'8aJ�{D��~xx���L*�7xȇ��d��Fi�qQ�|uvh�N�f��[6b.= �k��#.��b��Oj�;�P�]a��󉮼�8k��O����M��'��ܕJ�jj"ʠFP�/V�s�*���`�`0��ެ !rT����-��3Q�[�-l�y��L�MFU��E>�KKK#WKGE�ܸ��뜢��"2pk�*�"�}*e� č�f��7�m�t���p�v�ǐ~s�9B.�!��H���z���� w�e;�kŦ"B6g��"8������Ml�zԦ�u���qI�H&y��F��1�Yb�K`+aB0�Y�ؠ�pK�#3�`��$�-Y6 �H$��_#W:�γCex-�nC{0skY�мQ9�����^S�������� i͞u��ik#��Eu���٦�f��Lu��V]2L]�%�K�LC� ��8ks������'�����>Ni��hR�6p�Y�i?O�}����>B�Ā�&4��񣻺� ��y,<u�Ȃ���)�{��jmC�s���3"穆)�Nϗ!ܓU7� —yl�����+>��F�`�2:!&ZÔT&(>m���^�S�O��䥊������u�4#3�MD�ЫH���o�,y#3���BPt��\b��1)�:����6�}�����[���{P��%�Nx����;4NM��ä���sFS��L#34�y���Q Θ�����4g��-9��h �Zbm�4��P�1���u ^& w0�>x�E�735Y.�f�`��#W�׆�$��ȕ����70�SH�����v���6��Գ�� ���fH"�uvy0s���ݴߝB�G=e ����M ����θ���6w��#3��wL|���QA�JV���u�! ��(]�ri�%ɫ�jh�i�S�֚H�����->� �QB$} 4�Um��(5,Ѵ�+�� mT(���%��SZ4�Y��`��|��GoX#��=Tжa�X��2��23sBU����% �a�� t��1#39�h�M'P5���[)E*$��-�V��КK2j�T8���pfe���`�J…�-�3a�`d ͥ�kv��2�Kz�����#Wikn�g���#3`�QC�#3��#0�&�ԥ��%�����H�]����%C~<��H�V��������@����p⍝�#„�#A�t���Ñ��50#W���0b:3�6�1(#Wh�� 56�^��LDb�M#0�F��胷x�J7Av $EZ�>Y�ڨ����&cH���F�ci;�J���?@d�/;��9��PtEB4QL! �ۜ�u詭we��~�=Cp�s�rX���-#W��a#0Z,���@Ndݻ��m�‚N�į(Px}��sg���H�_��/n�!@߿ٞJ�[K�;c}d)TrD3���жi<噫dED2��#3�� ���fy�EI�n���q��?����M������s�Mz�k�㘓\!�y�#�&�FC9���K5���U]y뫒��U���`�� g.e+�J����R��%�((�a��'#WM�Ɲ�4�� �Ӝ2��}��J����Q���3���mw��6ۑ~4�[�z���ZE��4Ǘ�&�h\�a�1�J7�������)�QqO�LH�l�کط@/a�V�k�F�|4��\n����,��[,^,�:��B �S�_�J �TOm2���59�He�AˑVе���'� #3��d���s���j��Eܡ6��|��l_�y�:�Nι;��D��v��Ti����dk�W#0��O�6�d�[[��y�]ռ �!2Je�KN�5��њ�]�G�<�W.�u޻� ��Q$, A��Tb��*0�SZ4(���,#0@|دfI�IA�q.�pb�%���'�K3whѫ�.��l�� ��u"�̲�D�EO��M gW���;�o�!�<O#3Z�#W�Ѐ��S�s[G��c8?h�P��,�����m���#P�#3�<w0��B�CEi+�D�)�x|í��#3lIA�h��(7��Mbtx����G�-;��� tO ��pϒŠ�D�g?LV����\|��OF\��#?,�@���ð4�#3fɡ@�B�� jp�^ 2��U��S�45P�#3;��0&�/��~��i��T�Fi�e�}���2�d۬d��#3��.>h�'(��PPv�E z��z��M]XՃ�x$� �`\��r(�8f>6��Z˖�������Ay �;�+4�L�C&I2���_c#3���A���EX�#3���~L�A~�J ��4�����U2�$�X�6�Ҙ�k&��gRk%�쭮����@ E�PD����{,�QdE�A[�G "@X!EW�";�{&Z򢅥HXr� #0�TCg�U������|}_U�k�l��D�:�����ԡh*P߅�f��VEF@Q�P5��v��:����3��裎�+�7n���ѣ�CX�w��+�㤯��|+oss1��4T��=R@h�FŠ!H��2R#WԹH�h�#0�#eq6A¢D�du4��Z1*ƫ6�L�P�2^�ɵ]��Ү�ܼݮ�m�n"��M��Q�׾ʾ���tpߦ�6�x[`H#W��J��R���`9�Z����x��/BX�"��w}>ڎ�I���pҵ Ma�i��� ��g�d�Z׮��7�7�6N���"�rĀb���E�8l�7������Q��Y�TY��p���"��$�%E�$(f�j[%�����@M��<&���a��B�I���&��1�P�s�~0cߟ1���¡�Q-xʻ��t�hQbI&& ��j�4T�Mw"�I�  ����Ӗ05�e㝜LKj!�#32�̮�*���}^�>�z�"��ϮIa�H$ SIL���v��~��;t�\ ����"Oճ���1O:����DK%�Q����"3m������K*�1�%(emlV�5�Ѝ+1��U����s]0�#)O�+�t�'�8�U�#@6ylF��D8�}3�jsCD��|@��v\��N�#0'hC`�z0��VF@$0���F����>�-�HG�p�$���T>��z�}�!T�J��/�=}0�C�������� ��׮J��ϷY�e.�5J���<ߛ���z�r�h��E=3�vwwHƒra߅�jn�TQ��܁��˹,: 6Pj�e-.:�:��(�9%|�";3��[4=$�CY��*����ێ6�6�a�Ӆ&���o�6�ŸA�Pו���!$А�;^p��l�g<��K�;����#��CyBU�nCMd�5� l��.����ENz鍥,�He�F�A�#3��d�TkH��.� z&fs4�0lk'�]~�΅a.�n�B͏ij#0���E��yz|��K��^⹬��iU�Т�Q��)$#$� #�������!�I��Ӗ2:X�L�#W"t��<� ��,u �A����t���xy���)#0#W�s8��ļ-�Q$C�hr#��@����j#W}��������BiJ��(�Y���]��������OFN����#W�y��4��낣��G���xM��B�� �N2dE3��x�!��������x��Fe6�� ��~�MW���#0_w�������j����/�#0q�>N�'� � �m%�5�?�����s^�0\��� ���ҧ�#6��k#0#3C�(��G��<�*�}�W�m�����1��u�I�4��w\�����}]y����ޞ-x��o6��qm8II�iM�a�-Q�n���#W�6M��+^u��!�v���%�[�V��ּ��(4!�!���S`�D1�m-�g��;�%U|J�{,x�/fz�����y�>w��y��ßLd$AI5J�JYmK��[~{����j�~CQ*lHTjR66�!K2U6����Q���s#3"(щ���1Y$P{P==�7�FE�4a�#0-�8~�jJ���bA���y�O3��pG�BE���+|%z�w\ٍ65�[+b�cfF-��*iE����x�i6ԋ5�A��d���B��89_�U6��N�gPz>�����x�y^��d�����\i��N��_��A1 > �D��b!�"F=����B�#W:i2��J��z�<�!��MW��-�`�J���v4����Q`3̬�=�0&�#W�#W���~�:C�ϳ!h����Ǭ`$͒vD�T�Or�$�"k������E5�5stkTKZK(�1�dCIR�� H`�Nl�-��#�ZH(l� �#W�lk �Ti�$D$Fh-�^�f]Z��t ������a-Y�tj�3����Lf�`% gvq-)A�/7����հMO�&���U���%j�|��?ӣ�\��:�׌lc����6#�& �~�4:#0G���P���%rN� yQ�%�Ji�I��QKc�ڤ�aKF�Km���hQZ6�� �"� "3ü���gl�@�ADH��0P���%ر���#� �b,a�HЂ�Ʌ4SD�ٴ�XYA)w�#0��{�n����E�V�eJ����U�h+J��j͖"�6ڌA��O�(�8c���������VY) ~s?� P�@�a������>���)���6��k��Y�#W#3o�$~�q�yi�-����F�ٖ!��������1��wk0rE4Y�ТpQ#0�-,�P(e�o��f�SE*[�l+���k���k��k�mKoM\��wY��̪뛶��v��ɖZ�����6����[&ʒ�D�Ʒ�ռ�^ur��F.JE��" �0<���ل^�חU)��jL�W�k�����;V�[e&S*��[)k�w-���X�l�k��mv���{�P�Hh�ۈ#W�#h�]8!��kƪ�J� BŐ�>��!l��Ȗ�T� B�,�#3���4�#3�7���[i�#3���mf�� S4��ɷ�Z���u���U� ��j���r�w#0;��I��'QO�bz��T.(X�Æ0)8I�o����p�B�Q�lz7�q!n�x���H�] �T�Ɇ��N.���;E�8[��4ƚ+�>�K\�; >�8�$QG�)�ļ.#3��Lq�)Ѯb�x�$�b#W�'T����R7�8�;��3��w˽l�{����6RK��p @���kW���IYw]*OM��C�m�r��(<�P�#Wj�2���c6�:�EV-��V�(t�K@�Xt4�F��ͽ��\c��<-[!d+�~�ɖf � �0I��dT��*d>W�����)���J'`O8)�A�.Y��IԒ�*) ���92�3؝�l#3L";6�f#W c�4;d�>��+�<��),�,ԛE^-�)M���J���a~���~�b��a�v����#�U�m���A.�q��/��m�^HòP�N�1��ЁB�{�')��[��!D�k� ���Q�.�r����e0���hr"J b"+3�P'l�Wή�z6�c�(4���5EUy�Q�h�+l��qU��8twڅ��[��NT}�;������t��H�h��l���;Qߐ�7�4�Bȓi�E����HHt�)FS��;��fZ��ia�#0#3 "k=��v�+mg�zy�c�'E�FG��_h��$ �`���#P��!#0@�"!kB�:�Y�z�5��Z��ĭt�e�njR�E'ٜЀZ��W�<�@aT6�dm�t��C�#0k��}G�F�O\��e !\QG�쏝_�01�]��pGa�s?L�#0� ����!�w�?�ѧ�������������~�����z��|���������s�ǚ��y�g�?�����]�G������O�?�?��������K?�������O����}�e���?�����������`w�LGO�x� �@�; �&e\?��d#0a�*�%G#3S����"V�����- ���!芊�41��K�������ڦ��DDUDc�$�#0���#3j�l�2ٞ� ���!�=NHTȧ�����6xq����"u�����L�M��P��m����G��L���r���������mv�������$'�5�b��n_��5� �v��/��+�Qf(g����)�5#W8L/�e���*ّߩ���#�̣W�#�u������j��딥 �0=��BC �Z����5CÔ�t_��?>>O�~�>Í��9���Y䭙����,{R���_�S<���E��B���P)�����T�p������dO{Ň�F�•���&^A}�&�P�N���4+�ˉ2�T�[!uD�r�����7��<�ï�����{�j�͸��*������ަ0hT�an�9`��P����MVK�"�unɐ�c�2�����"�o����Ӣ�m�w�l��|3���d�cV�4}u�e�� ������e#W�)LY �1�#�\�����2� ����L��-q,�?B¶�y�x��3�:Fƍp�cE�<cH�H����yM�޳�Պ���8y��#P1'��wх��Z�T�w��`��H����0� �0�p���Y r�|ĹfQ#3K�AVB!�x�$q�#W7�ϖ����q�����=��v�"���1aP��yp��cݷYt����$d^�t�lsSO�iV�#0|P$��;��c��&f��65�BD�T"C^���K�(o�5�e�]D4���N��&%�����B{ed��8m�1�[�f#0�˖� ���^�in���'��ssIm��D�� ��j)����SI�m�X�H�aD�&�(#0 ��TO#3>r���J����$������StU�H("���"�<J�JT��� =[栜>���i,�v�x/�/�o����쭅�d>O�S�������{n;� K���]{:;�9���%h�&zQ0�f�9.� !��FU���!�|�O��t<8���r�q�*�\C����#0�ٙ��=$��)Ycj!�Fvt�q�4<�`PG�㶧�g��/�f|z�ܗ���N�jX�4{k~^&�#3�Q�Va~�-����w�6}y�!+tVB�_wJ�� i7��3��0�M�-�m�c[Ť#W�k\ԕ����h��-���^�V�4[Rlm�{��U�����H�}���U��tA�a��TQ6���>Z�����PǕ��r�� ݁Dw�X��8.�awA 3&����.$Ǡ���r�R����]-B��������D]@tPk�,�H��thB([���w�}l��SҮ�#3O�9 � ����qP����VTQV���e�|��[�M�v�i��*,A1�RAi@�k� E�B& '���"�(@�H���ϫ�hk!��|��@D�v\�A;6�|��=� G�ޔ#08!x&�Du�d�OTO��A��~/�����/��?�B�+A�x�RI��#�d#W�����y������?�v���Q��Ĵ&=]_��_�6o���?ٿc~�_xLc�������O�~?g����fNjuGBs��<�x○d|�wp1�<�����/�O �~���m>�1����“���"^9E!���|O�^?��,���G�7E�óG?�5����g1r�t�f���u��*�������5�K�!�a(�5(��E��t��'DS8&�j�\e����|�!���t��tYX�\�i�#pX��a(߇�h�TK^�ӘH0����F`�k�X�<��W�{n��X��&������,���ϲ�鹿S�����g�z��)��,����X~�bI(@ �P/M�5=��D�OH���B^���#0���w$S� '�p
+#<==
diff --git a/.gitignore b/waflib/.gitignore
index 8d35cb3..8d35cb3 100644
--- a/.gitignore
+++ b/waflib/.gitignore
diff --git a/Build.py b/waflib/Build.py
index 1afcba6..1afcba6 100644
--- a/Build.py
+++ b/waflib/Build.py
diff --git a/waflib/COPYING b/waflib/COPYING
new file mode 100644
index 0000000..a4147d2
--- /dev/null
+++ b/waflib/COPYING
@@ -0,0 +1,25 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/ConfigSet.py b/waflib/ConfigSet.py
index b300bb5..b300bb5 100644
--- a/ConfigSet.py
+++ b/waflib/ConfigSet.py
diff --git a/Configure.py b/waflib/Configure.py
index d0a4793..d0a4793 100644
--- a/Configure.py
+++ b/waflib/Configure.py
diff --git a/Context.py b/waflib/Context.py
index bb47c92..bb47c92 100644
--- a/Context.py
+++ b/waflib/Context.py
diff --git a/Errors.py b/waflib/Errors.py
index bf75c1b..bf75c1b 100644
--- a/Errors.py
+++ b/waflib/Errors.py
diff --git a/Logs.py b/waflib/Logs.py
index 2a47516..2a47516 100644
--- a/Logs.py
+++ b/waflib/Logs.py
diff --git a/Node.py b/waflib/Node.py
index 4ac1ea8..4ac1ea8 100644
--- a/Node.py
+++ b/waflib/Node.py
diff --git a/Options.py b/waflib/Options.py
index ad802d4..ad802d4 100644
--- a/Options.py
+++ b/waflib/Options.py
diff --git a/README.md b/waflib/README.md
index c5361b9..c5361b9 100644
--- a/README.md
+++ b/waflib/README.md
diff --git a/Runner.py b/waflib/Runner.py
index 261084d..261084d 100644
--- a/Runner.py
+++ b/waflib/Runner.py
diff --git a/Scripting.py b/waflib/Scripting.py
index 749d4f2..749d4f2 100644
--- a/Scripting.py
+++ b/waflib/Scripting.py
diff --git a/Task.py b/waflib/Task.py
index 0fc449d..0fc449d 100644
--- a/Task.py
+++ b/waflib/Task.py
diff --git a/TaskGen.py b/waflib/TaskGen.py
index a74e643..a74e643 100644
--- a/TaskGen.py
+++ b/waflib/TaskGen.py
diff --git a/Tools/__init__.py b/waflib/Tools/__init__.py
index 079df35..079df35 100644
--- a/Tools/__init__.py
+++ b/waflib/Tools/__init__.py
diff --git a/Tools/ar.py b/waflib/Tools/ar.py
index b39b645..b39b645 100644
--- a/Tools/ar.py
+++ b/waflib/Tools/ar.py
diff --git a/Tools/asm.py b/waflib/Tools/asm.py
index b6f26fb..b6f26fb 100644
--- a/Tools/asm.py
+++ b/waflib/Tools/asm.py
diff --git a/Tools/bison.py b/waflib/Tools/bison.py
index eef56dc..eef56dc 100644
--- a/Tools/bison.py
+++ b/waflib/Tools/bison.py
diff --git a/Tools/c.py b/waflib/Tools/c.py
index effd6b6..effd6b6 100644
--- a/Tools/c.py
+++ b/waflib/Tools/c.py
diff --git a/Tools/c_aliases.py b/waflib/Tools/c_aliases.py
index c9d5369..c9d5369 100644
--- a/Tools/c_aliases.py
+++ b/waflib/Tools/c_aliases.py
diff --git a/Tools/c_config.py b/waflib/Tools/c_config.py
index d2b3c0d..d2b3c0d 100644
--- a/Tools/c_config.py
+++ b/waflib/Tools/c_config.py
diff --git a/Tools/c_osx.py b/waflib/Tools/c_osx.py
index f70b128..f70b128 100644
--- a/Tools/c_osx.py
+++ b/waflib/Tools/c_osx.py
diff --git a/Tools/c_preproc.py b/waflib/Tools/c_preproc.py
index 7e04b4a..7e04b4a 100644
--- a/Tools/c_preproc.py
+++ b/waflib/Tools/c_preproc.py
diff --git a/Tools/c_tests.py b/waflib/Tools/c_tests.py
index f858df5..f858df5 100644
--- a/Tools/c_tests.py
+++ b/waflib/Tools/c_tests.py
diff --git a/Tools/ccroot.py b/waflib/Tools/ccroot.py
index cfef8bf..cfef8bf 100644
--- a/Tools/ccroot.py
+++ b/waflib/Tools/ccroot.py
diff --git a/Tools/clang.py b/waflib/Tools/clang.py
index 3828e39..3828e39 100644
--- a/Tools/clang.py
+++ b/waflib/Tools/clang.py
diff --git a/Tools/clangxx.py b/waflib/Tools/clangxx.py
index 152013c..152013c 100644
--- a/Tools/clangxx.py
+++ b/waflib/Tools/clangxx.py
diff --git a/Tools/compiler_c.py b/waflib/Tools/compiler_c.py
index 2dba3f8..2dba3f8 100644
--- a/Tools/compiler_c.py
+++ b/waflib/Tools/compiler_c.py
diff --git a/Tools/compiler_cxx.py b/waflib/Tools/compiler_cxx.py
index 1af65a2..1af65a2 100644
--- a/Tools/compiler_cxx.py
+++ b/waflib/Tools/compiler_cxx.py
diff --git a/Tools/compiler_d.py b/waflib/Tools/compiler_d.py
index 43bb1f6..43bb1f6 100644
--- a/Tools/compiler_d.py
+++ b/waflib/Tools/compiler_d.py
diff --git a/Tools/compiler_fc.py b/waflib/Tools/compiler_fc.py
index 96b58e7..96b58e7 100644
--- a/Tools/compiler_fc.py
+++ b/waflib/Tools/compiler_fc.py
diff --git a/Tools/cs.py b/waflib/Tools/cs.py
index aecca6d..aecca6d 100644
--- a/Tools/cs.py
+++ b/waflib/Tools/cs.py
diff --git a/Tools/cxx.py b/waflib/Tools/cxx.py
index 194fad7..194fad7 100644
--- a/Tools/cxx.py
+++ b/waflib/Tools/cxx.py
diff --git a/Tools/d.py b/waflib/Tools/d.py
index e4cf73b..e4cf73b 100644
--- a/Tools/d.py
+++ b/waflib/Tools/d.py
diff --git a/Tools/d_config.py b/waflib/Tools/d_config.py
index 6637556..6637556 100644
--- a/Tools/d_config.py
+++ b/waflib/Tools/d_config.py
diff --git a/Tools/d_scan.py b/waflib/Tools/d_scan.py
index 14c6c31..14c6c31 100644
--- a/Tools/d_scan.py
+++ b/waflib/Tools/d_scan.py
diff --git a/Tools/dbus.py b/waflib/Tools/dbus.py
index d520f1c..d520f1c 100644
--- a/Tools/dbus.py
+++ b/waflib/Tools/dbus.py
diff --git a/Tools/dmd.py b/waflib/Tools/dmd.py
index 8917ca1..8917ca1 100644
--- a/Tools/dmd.py
+++ b/waflib/Tools/dmd.py
diff --git a/Tools/errcheck.py b/waflib/Tools/errcheck.py
index de8d75a..de8d75a 100644
--- a/Tools/errcheck.py
+++ b/waflib/Tools/errcheck.py
diff --git a/Tools/fc.py b/waflib/Tools/fc.py
index d9e8d8c..d9e8d8c 100644
--- a/Tools/fc.py
+++ b/waflib/Tools/fc.py
diff --git a/Tools/fc_config.py b/waflib/Tools/fc_config.py
index 222f3a5..222f3a5 100644
--- a/Tools/fc_config.py
+++ b/waflib/Tools/fc_config.py
diff --git a/Tools/fc_scan.py b/waflib/Tools/fc_scan.py
index 12cb0fc..12cb0fc 100644
--- a/Tools/fc_scan.py
+++ b/waflib/Tools/fc_scan.py
diff --git a/Tools/flex.py b/waflib/Tools/flex.py
index 2256657..2256657 100644
--- a/Tools/flex.py
+++ b/waflib/Tools/flex.py
diff --git a/Tools/g95.py b/waflib/Tools/g95.py
index f69ba4f..f69ba4f 100644
--- a/Tools/g95.py
+++ b/waflib/Tools/g95.py
diff --git a/Tools/gas.py b/waflib/Tools/gas.py
index 77afed7..77afed7 100644
--- a/Tools/gas.py
+++ b/waflib/Tools/gas.py
diff --git a/Tools/gcc.py b/waflib/Tools/gcc.py
index acdd473..acdd473 100644
--- a/Tools/gcc.py
+++ b/waflib/Tools/gcc.py
diff --git a/Tools/gdc.py b/waflib/Tools/gdc.py
index d89a66d..d89a66d 100644
--- a/Tools/gdc.py
+++ b/waflib/Tools/gdc.py
diff --git a/Tools/gfortran.py b/waflib/Tools/gfortran.py
index 1050667..1050667 100644
--- a/Tools/gfortran.py
+++ b/waflib/Tools/gfortran.py
diff --git a/Tools/glib2.py b/waflib/Tools/glib2.py
index 949fe37..949fe37 100644
--- a/Tools/glib2.py
+++ b/waflib/Tools/glib2.py
diff --git a/Tools/gnu_dirs.py b/waflib/Tools/gnu_dirs.py
index 2847071..2847071 100644
--- a/Tools/gnu_dirs.py
+++ b/waflib/Tools/gnu_dirs.py
diff --git a/Tools/gxx.py b/waflib/Tools/gxx.py
index 22c5d26..22c5d26 100644
--- a/Tools/gxx.py
+++ b/waflib/Tools/gxx.py
diff --git a/Tools/icc.py b/waflib/Tools/icc.py
index b6492c8..b6492c8 100644
--- a/Tools/icc.py
+++ b/waflib/Tools/icc.py
diff --git a/Tools/icpc.py b/waflib/Tools/icpc.py
index 8a6cc6c..8a6cc6c 100644
--- a/Tools/icpc.py
+++ b/waflib/Tools/icpc.py
diff --git a/Tools/ifort.py b/waflib/Tools/ifort.py
index 74934f3..74934f3 100644
--- a/Tools/ifort.py
+++ b/waflib/Tools/ifort.py
diff --git a/Tools/intltool.py b/waflib/Tools/intltool.py
index af95ba8..af95ba8 100644
--- a/Tools/intltool.py
+++ b/waflib/Tools/intltool.py
diff --git a/Tools/irixcc.py b/waflib/Tools/irixcc.py
index c3ae1ac..c3ae1ac 100644
--- a/Tools/irixcc.py
+++ b/waflib/Tools/irixcc.py
diff --git a/Tools/javaw.py b/waflib/Tools/javaw.py
index f6fd20c..f6fd20c 100644
--- a/Tools/javaw.py
+++ b/waflib/Tools/javaw.py
diff --git a/Tools/ldc2.py b/waflib/Tools/ldc2.py
index a51c344..a51c344 100644
--- a/Tools/ldc2.py
+++ b/waflib/Tools/ldc2.py
diff --git a/Tools/lua.py b/waflib/Tools/lua.py
index 15a333a..15a333a 100644
--- a/Tools/lua.py
+++ b/waflib/Tools/lua.py
diff --git a/Tools/md5_tstamp.py b/waflib/Tools/md5_tstamp.py
index 6428e46..6428e46 100644
--- a/Tools/md5_tstamp.py
+++ b/waflib/Tools/md5_tstamp.py
diff --git a/Tools/msvc.py b/waflib/Tools/msvc.py
index 17b347d..17b347d 100644
--- a/Tools/msvc.py
+++ b/waflib/Tools/msvc.py
diff --git a/Tools/nasm.py b/waflib/Tools/nasm.py
index 411d582..411d582 100644
--- a/Tools/nasm.py
+++ b/waflib/Tools/nasm.py
diff --git a/Tools/nobuild.py b/waflib/Tools/nobuild.py
index 2e4b055..2e4b055 100644
--- a/Tools/nobuild.py
+++ b/waflib/Tools/nobuild.py
diff --git a/Tools/perl.py b/waflib/Tools/perl.py
index 32b03fb..32b03fb 100644
--- a/Tools/perl.py
+++ b/waflib/Tools/perl.py
diff --git a/Tools/python.py b/waflib/Tools/python.py
index 25841d0..25841d0 100644
--- a/Tools/python.py
+++ b/waflib/Tools/python.py
diff --git a/Tools/qt5.py b/waflib/Tools/qt5.py
index 4f9c690..4f9c690 100644
--- a/Tools/qt5.py
+++ b/waflib/Tools/qt5.py
diff --git a/Tools/ruby.py b/waflib/Tools/ruby.py
index 8d92a79..8d92a79 100644
--- a/Tools/ruby.py
+++ b/waflib/Tools/ruby.py
diff --git a/Tools/suncc.py b/waflib/Tools/suncc.py
index 33d34fc..33d34fc 100644
--- a/Tools/suncc.py
+++ b/waflib/Tools/suncc.py
diff --git a/Tools/suncxx.py b/waflib/Tools/suncxx.py
index 3b384f6..3b384f6 100644
--- a/Tools/suncxx.py
+++ b/waflib/Tools/suncxx.py
diff --git a/Tools/tex.py b/waflib/Tools/tex.py
index eaf9fdb..eaf9fdb 100644
--- a/Tools/tex.py
+++ b/waflib/Tools/tex.py
diff --git a/Tools/vala.py b/waflib/Tools/vala.py
index 822ec50..822ec50 100644
--- a/Tools/vala.py
+++ b/waflib/Tools/vala.py
diff --git a/Tools/waf_unit_test.py b/waflib/Tools/waf_unit_test.py
index a71ed1c..a71ed1c 100644
--- a/Tools/waf_unit_test.py
+++ b/waflib/Tools/waf_unit_test.py
diff --git a/Tools/winres.py b/waflib/Tools/winres.py
index 586c596..586c596 100644
--- a/Tools/winres.py
+++ b/waflib/Tools/winres.py
diff --git a/Tools/xlc.py b/waflib/Tools/xlc.py
index 134dd41..134dd41 100644
--- a/Tools/xlc.py
+++ b/waflib/Tools/xlc.py
diff --git a/Tools/xlcxx.py b/waflib/Tools/xlcxx.py
index 76aa59b..76aa59b 100644
--- a/Tools/xlcxx.py
+++ b/waflib/Tools/xlcxx.py
diff --git a/Utils.py b/waflib/Utils.py
index b4665c4..b4665c4 100644
--- a/Utils.py
+++ b/waflib/Utils.py
diff --git a/__init__.py b/waflib/__init__.py
index 079df35..079df35 100644
--- a/__init__.py
+++ b/waflib/__init__.py
diff --git a/ansiterm.py b/waflib/ansiterm.py
index 0d20c63..0d20c63 100644
--- a/ansiterm.py
+++ b/waflib/ansiterm.py
diff --git a/extras/__init__.py b/waflib/extras/__init__.py
index c8a3c34..c8a3c34 100644
--- a/extras/__init__.py
+++ b/waflib/extras/__init__.py
diff --git a/extras/autowaf.py b/waflib/extras/autowaf.py
index a4a06ba..a4a06ba 100644
--- a/extras/autowaf.py
+++ b/waflib/extras/autowaf.py
diff --git a/extras/batched_cc.py b/waflib/extras/batched_cc.py
index aad2872..aad2872 100644
--- a/extras/batched_cc.py
+++ b/waflib/extras/batched_cc.py
diff --git a/extras/biber.py b/waflib/extras/biber.py
index fd9db4e..fd9db4e 100644
--- a/extras/biber.py
+++ b/waflib/extras/biber.py
diff --git a/extras/bjam.py b/waflib/extras/bjam.py
index 8e04d3a..8e04d3a 100644
--- a/extras/bjam.py
+++ b/waflib/extras/bjam.py
diff --git a/extras/blender.py b/waflib/extras/blender.py
index e5efc28..e5efc28 100644
--- a/extras/blender.py
+++ b/waflib/extras/blender.py
diff --git a/extras/boo.py b/waflib/extras/boo.py
index 06623d4..06623d4 100644
--- a/extras/boo.py
+++ b/waflib/extras/boo.py
diff --git a/extras/boost.py b/waflib/extras/boost.py
index c2aaaa9..c2aaaa9 100644
--- a/extras/boost.py
+++ b/waflib/extras/boost.py
diff --git a/extras/build_file_tracker.py b/waflib/extras/build_file_tracker.py
index c4f26fd..c4f26fd 100644
--- a/extras/build_file_tracker.py
+++ b/waflib/extras/build_file_tracker.py
diff --git a/extras/build_logs.py b/waflib/extras/build_logs.py
index cdf8ed0..cdf8ed0 100644
--- a/extras/build_logs.py
+++ b/waflib/extras/build_logs.py
diff --git a/extras/buildcopy.py b/waflib/extras/buildcopy.py
index a6d9ac8..a6d9ac8 100644
--- a/extras/buildcopy.py
+++ b/waflib/extras/buildcopy.py
diff --git a/extras/c_bgxlc.py b/waflib/extras/c_bgxlc.py
index 6e3eaf7..6e3eaf7 100644
--- a/extras/c_bgxlc.py
+++ b/waflib/extras/c_bgxlc.py
diff --git a/extras/c_dumbpreproc.py b/waflib/extras/c_dumbpreproc.py
index ce9e1a4..ce9e1a4 100644
--- a/extras/c_dumbpreproc.py
+++ b/waflib/extras/c_dumbpreproc.py
diff --git a/extras/c_emscripten.py b/waflib/extras/c_emscripten.py
index e1ac494..e1ac494 100644
--- a/extras/c_emscripten.py
+++ b/waflib/extras/c_emscripten.py
diff --git a/extras/c_nec.py b/waflib/extras/c_nec.py
index 96bfae4..96bfae4 100644
--- a/extras/c_nec.py
+++ b/waflib/extras/c_nec.py
diff --git a/extras/cabal.py b/waflib/extras/cabal.py
index e10a0d1..e10a0d1 100644
--- a/extras/cabal.py
+++ b/waflib/extras/cabal.py
diff --git a/extras/cfg_altoptions.py b/waflib/extras/cfg_altoptions.py
index 47b1189..47b1189 100644
--- a/extras/cfg_altoptions.py
+++ b/waflib/extras/cfg_altoptions.py
diff --git a/extras/clang_compilation_database.py b/waflib/extras/clang_compilation_database.py
index 4d9b5e2..4d9b5e2 100644
--- a/extras/clang_compilation_database.py
+++ b/waflib/extras/clang_compilation_database.py
diff --git a/extras/codelite.py b/waflib/extras/codelite.py
index 523302c..523302c 100644
--- a/extras/codelite.py
+++ b/waflib/extras/codelite.py
diff --git a/extras/color_gcc.py b/waflib/extras/color_gcc.py
index b68c5eb..b68c5eb 100644
--- a/extras/color_gcc.py
+++ b/waflib/extras/color_gcc.py
diff --git a/extras/color_rvct.py b/waflib/extras/color_rvct.py
index f89ccbd..f89ccbd 100644
--- a/extras/color_rvct.py
+++ b/waflib/extras/color_rvct.py
diff --git a/extras/compat15.py b/waflib/extras/compat15.py
index 0e74df8..0e74df8 100644
--- a/extras/compat15.py
+++ b/waflib/extras/compat15.py
diff --git a/extras/cppcheck.py b/waflib/extras/cppcheck.py
index 13ff424..13ff424 100644
--- a/extras/cppcheck.py
+++ b/waflib/extras/cppcheck.py
diff --git a/extras/cpplint.py b/waflib/extras/cpplint.py
index e3302e5..e3302e5 100644
--- a/extras/cpplint.py
+++ b/waflib/extras/cpplint.py
diff --git a/extras/cross_gnu.py b/waflib/extras/cross_gnu.py
index 309f53b..309f53b 100644
--- a/extras/cross_gnu.py
+++ b/waflib/extras/cross_gnu.py
diff --git a/extras/cython.py b/waflib/extras/cython.py
index 481d6f4..481d6f4 100644
--- a/extras/cython.py
+++ b/waflib/extras/cython.py
diff --git a/extras/dcc.py b/waflib/extras/dcc.py
index c1a57c0..c1a57c0 100644
--- a/extras/dcc.py
+++ b/waflib/extras/dcc.py
diff --git a/extras/distnet.py b/waflib/extras/distnet.py
index 09a31a6..09a31a6 100644
--- a/extras/distnet.py
+++ b/waflib/extras/distnet.py
diff --git a/extras/doxygen.py b/waflib/extras/doxygen.py
index 28f56e9..28f56e9 100644
--- a/extras/doxygen.py
+++ b/waflib/extras/doxygen.py
diff --git a/extras/dpapi.py b/waflib/extras/dpapi.py
index b94d482..b94d482 100644
--- a/extras/dpapi.py
+++ b/waflib/extras/dpapi.py
diff --git a/extras/eclipse.py b/waflib/extras/eclipse.py
index bb78741..bb78741 100644
--- a/extras/eclipse.py
+++ b/waflib/extras/eclipse.py
diff --git a/extras/erlang.py b/waflib/extras/erlang.py
index 49f6d5b..49f6d5b 100644
--- a/extras/erlang.py
+++ b/waflib/extras/erlang.py
diff --git a/extras/fast_partial.py b/waflib/extras/fast_partial.py
index b3af513..b3af513 100644
--- a/extras/fast_partial.py
+++ b/waflib/extras/fast_partial.py
diff --git a/extras/fc_bgxlf.py b/waflib/extras/fc_bgxlf.py
index cca1810..cca1810 100644
--- a/extras/fc_bgxlf.py
+++ b/waflib/extras/fc_bgxlf.py
diff --git a/extras/fc_cray.py b/waflib/extras/fc_cray.py
index da733fa..da733fa 100644
--- a/extras/fc_cray.py
+++ b/waflib/extras/fc_cray.py
diff --git a/extras/fc_nag.py b/waflib/extras/fc_nag.py
index edcb218..edcb218 100644
--- a/extras/fc_nag.py
+++ b/waflib/extras/fc_nag.py
diff --git a/extras/fc_nec.py b/waflib/extras/fc_nec.py
index 67c8680..67c8680 100644
--- a/extras/fc_nec.py
+++ b/waflib/extras/fc_nec.py
diff --git a/extras/fc_open64.py b/waflib/extras/fc_open64.py
index 413719f..413719f 100644
--- a/extras/fc_open64.py
+++ b/waflib/extras/fc_open64.py
diff --git a/extras/fc_pgfortran.py b/waflib/extras/fc_pgfortran.py
index afb2817..afb2817 100644
--- a/extras/fc_pgfortran.py
+++ b/waflib/extras/fc_pgfortran.py
diff --git a/extras/fc_solstudio.py b/waflib/extras/fc_solstudio.py
index 53766df..53766df 100644
--- a/extras/fc_solstudio.py
+++ b/waflib/extras/fc_solstudio.py
diff --git a/extras/fc_xlf.py b/waflib/extras/fc_xlf.py
index 5a3da03..5a3da03 100644
--- a/extras/fc_xlf.py
+++ b/waflib/extras/fc_xlf.py
diff --git a/extras/file_to_object.py b/waflib/extras/file_to_object.py
index 1393b51..1393b51 100644
--- a/extras/file_to_object.py
+++ b/waflib/extras/file_to_object.py
diff --git a/extras/fluid.py b/waflib/extras/fluid.py
index 4814a35..4814a35 100644
--- a/extras/fluid.py
+++ b/waflib/extras/fluid.py
diff --git a/extras/freeimage.py b/waflib/extras/freeimage.py
index f27e525..f27e525 100644
--- a/extras/freeimage.py
+++ b/waflib/extras/freeimage.py
diff --git a/extras/fsb.py b/waflib/extras/fsb.py
index 1b8f398..1b8f398 100644
--- a/extras/fsb.py
+++ b/waflib/extras/fsb.py
diff --git a/extras/fsc.py b/waflib/extras/fsc.py
index c67e70b..c67e70b 100644
--- a/extras/fsc.py
+++ b/waflib/extras/fsc.py
diff --git a/extras/gccdeps.py b/waflib/extras/gccdeps.py
index d9758ab..d9758ab 100644
--- a/extras/gccdeps.py
+++ b/waflib/extras/gccdeps.py
diff --git a/extras/gdbus.py b/waflib/extras/gdbus.py
index 0e0476e..0e0476e 100644
--- a/extras/gdbus.py
+++ b/waflib/extras/gdbus.py
diff --git a/extras/gob2.py b/waflib/extras/gob2.py
index b4fa3b9..b4fa3b9 100644
--- a/extras/gob2.py
+++ b/waflib/extras/gob2.py
diff --git a/extras/halide.py b/waflib/extras/halide.py
index 6078e38..6078e38 100644
--- a/extras/halide.py
+++ b/waflib/extras/halide.py
diff --git a/extras/javatest.py b/waflib/extras/javatest.py
index 979b8d8..979b8d8 100755
--- a/extras/javatest.py
+++ b/waflib/extras/javatest.py
diff --git a/extras/kde4.py b/waflib/extras/kde4.py
index e49a9ec..e49a9ec 100644
--- a/extras/kde4.py
+++ b/waflib/extras/kde4.py
diff --git a/extras/local_rpath.py b/waflib/extras/local_rpath.py
index b2507e1..b2507e1 100644
--- a/extras/local_rpath.py
+++ b/waflib/extras/local_rpath.py
diff --git a/extras/lv2.py b/waflib/extras/lv2.py
index 815987f..815987f 100644
--- a/extras/lv2.py
+++ b/waflib/extras/lv2.py
diff --git a/extras/make.py b/waflib/extras/make.py
index 933d9ca..933d9ca 100644
--- a/extras/make.py
+++ b/waflib/extras/make.py
diff --git a/extras/midl.py b/waflib/extras/midl.py
index 43e6cf9..43e6cf9 100644
--- a/extras/midl.py
+++ b/waflib/extras/midl.py
diff --git a/extras/msvcdeps.py b/waflib/extras/msvcdeps.py
index fc1ecd4..fc1ecd4 100644
--- a/extras/msvcdeps.py
+++ b/waflib/extras/msvcdeps.py
diff --git a/extras/msvs.py b/waflib/extras/msvs.py
index 8aa2db0..8aa2db0 100644
--- a/extras/msvs.py
+++ b/waflib/extras/msvs.py
diff --git a/extras/netcache_client.py b/waflib/extras/netcache_client.py
index dc49048..dc49048 100644
--- a/extras/netcache_client.py
+++ b/waflib/extras/netcache_client.py
diff --git a/extras/objcopy.py b/waflib/extras/objcopy.py
index 82d8359..82d8359 100644
--- a/extras/objcopy.py
+++ b/waflib/extras/objcopy.py
diff --git a/extras/ocaml.py b/waflib/extras/ocaml.py
index afe73c0..afe73c0 100644
--- a/extras/ocaml.py
+++ b/waflib/extras/ocaml.py
diff --git a/extras/package.py b/waflib/extras/package.py
index c06498e..c06498e 100644
--- a/extras/package.py
+++ b/waflib/extras/package.py
diff --git a/extras/parallel_debug.py b/waflib/extras/parallel_debug.py
index 35883a3..35883a3 100644
--- a/extras/parallel_debug.py
+++ b/waflib/extras/parallel_debug.py
diff --git a/extras/pch.py b/waflib/extras/pch.py
index 103e752..103e752 100644
--- a/extras/pch.py
+++ b/waflib/extras/pch.py
diff --git a/extras/pep8.py b/waflib/extras/pep8.py
index 676beed..676beed 100644
--- a/extras/pep8.py
+++ b/waflib/extras/pep8.py
diff --git a/extras/pgicc.py b/waflib/extras/pgicc.py
index 9790b9c..9790b9c 100644
--- a/extras/pgicc.py
+++ b/waflib/extras/pgicc.py
diff --git a/extras/pgicxx.py b/waflib/extras/pgicxx.py
index eae121c..eae121c 100644
--- a/extras/pgicxx.py
+++ b/waflib/extras/pgicxx.py
diff --git a/extras/proc.py b/waflib/extras/proc.py
index 764abec..764abec 100644
--- a/extras/proc.py
+++ b/waflib/extras/proc.py
diff --git a/extras/protoc.py b/waflib/extras/protoc.py
index f3cb4d8..f3cb4d8 100644
--- a/extras/protoc.py
+++ b/waflib/extras/protoc.py
diff --git a/extras/pyqt5.py b/waflib/extras/pyqt5.py
index c21dfa7..c21dfa7 100644
--- a/extras/pyqt5.py
+++ b/waflib/extras/pyqt5.py
diff --git a/extras/pytest.py b/waflib/extras/pytest.py
index 7dd5a1a..7dd5a1a 100644
--- a/extras/pytest.py
+++ b/waflib/extras/pytest.py
diff --git a/extras/qnxnto.py b/waflib/extras/qnxnto.py
index 1158124..1158124 100644
--- a/extras/qnxnto.py
+++ b/waflib/extras/qnxnto.py
diff --git a/extras/qt4.py b/waflib/extras/qt4.py
index 90cae7e..90cae7e 100644
--- a/extras/qt4.py
+++ b/waflib/extras/qt4.py
diff --git a/extras/relocation.py b/waflib/extras/relocation.py
index 7e821f4..7e821f4 100644
--- a/extras/relocation.py
+++ b/waflib/extras/relocation.py
diff --git a/extras/remote.py b/waflib/extras/remote.py
index 3b038f7..3b038f7 100644
--- a/extras/remote.py
+++ b/waflib/extras/remote.py
diff --git a/extras/resx.py b/waflib/extras/resx.py
index caf4d31..caf4d31 100644
--- a/extras/resx.py
+++ b/waflib/extras/resx.py
diff --git a/extras/review.py b/waflib/extras/review.py
index 561e062..561e062 100644
--- a/extras/review.py
+++ b/waflib/extras/review.py
diff --git a/extras/rst.py b/waflib/extras/rst.py
index f3c3a5e..f3c3a5e 100644
--- a/extras/rst.py
+++ b/waflib/extras/rst.py
diff --git a/extras/run_do_script.py b/waflib/extras/run_do_script.py
index f3c5812..f3c5812 100644
--- a/extras/run_do_script.py
+++ b/waflib/extras/run_do_script.py
diff --git a/extras/run_m_script.py b/waflib/extras/run_m_script.py
index b5f27eb..b5f27eb 100644
--- a/extras/run_m_script.py
+++ b/waflib/extras/run_m_script.py
diff --git a/extras/run_py_script.py b/waflib/extras/run_py_script.py
index 3670381..3670381 100644
--- a/extras/run_py_script.py
+++ b/waflib/extras/run_py_script.py
diff --git a/extras/run_r_script.py b/waflib/extras/run_r_script.py
index b0d8f2b..b0d8f2b 100644
--- a/extras/run_r_script.py
+++ b/waflib/extras/run_r_script.py
diff --git a/extras/sas.py b/waflib/extras/sas.py
index 754c614..754c614 100644
--- a/extras/sas.py
+++ b/waflib/extras/sas.py
diff --git a/extras/satellite_assembly.py b/waflib/extras/satellite_assembly.py
index 005eb07..005eb07 100644
--- a/extras/satellite_assembly.py
+++ b/waflib/extras/satellite_assembly.py
diff --git a/extras/scala.py b/waflib/extras/scala.py
index a9880f0..a9880f0 100644
--- a/extras/scala.py
+++ b/waflib/extras/scala.py
diff --git a/extras/slow_qt4.py b/waflib/extras/slow_qt4.py
index ec7880b..ec7880b 100644
--- a/extras/slow_qt4.py
+++ b/waflib/extras/slow_qt4.py
diff --git a/extras/softlink_libs.py b/waflib/extras/softlink_libs.py
index 50c777f..50c777f 100644
--- a/extras/softlink_libs.py
+++ b/waflib/extras/softlink_libs.py
diff --git a/extras/stale.py b/waflib/extras/stale.py
index cac3f46..cac3f46 100644
--- a/extras/stale.py
+++ b/waflib/extras/stale.py
diff --git a/extras/stracedeps.py b/waflib/extras/stracedeps.py
index 37d82cb..37d82cb 100644
--- a/extras/stracedeps.py
+++ b/waflib/extras/stracedeps.py
diff --git a/extras/swig.py b/waflib/extras/swig.py
index fd3d6d2..fd3d6d2 100644
--- a/extras/swig.py
+++ b/waflib/extras/swig.py
diff --git a/extras/syms.py b/waflib/extras/syms.py
index dfa0059..dfa0059 100644
--- a/extras/syms.py
+++ b/waflib/extras/syms.py
diff --git a/extras/ticgt.py b/waflib/extras/ticgt.py
index f43a7ea..f43a7ea 100644
--- a/extras/ticgt.py
+++ b/waflib/extras/ticgt.py
diff --git a/extras/unity.py b/waflib/extras/unity.py
index 78128ed..78128ed 100644
--- a/extras/unity.py
+++ b/waflib/extras/unity.py
diff --git a/extras/use_config.py b/waflib/extras/use_config.py
index ef5129f..ef5129f 100644
--- a/extras/use_config.py
+++ b/waflib/extras/use_config.py
diff --git a/extras/valadoc.py b/waflib/extras/valadoc.py
index c50f69e..c50f69e 100644
--- a/extras/valadoc.py
+++ b/waflib/extras/valadoc.py
diff --git a/extras/waf_xattr.py b/waflib/extras/waf_xattr.py
index 351dd63..351dd63 100644
--- a/extras/waf_xattr.py
+++ b/waflib/extras/waf_xattr.py
diff --git a/extras/why.py b/waflib/extras/why.py
index 1bb941f..1bb941f 100644
--- a/extras/why.py
+++ b/waflib/extras/why.py
diff --git a/extras/win32_opts.py b/waflib/extras/win32_opts.py
index 9f7443c..9f7443c 100644
--- a/extras/win32_opts.py
+++ b/waflib/extras/win32_opts.py
diff --git a/extras/wix.py b/waflib/extras/wix.py
index d87bfbb..d87bfbb 100644
--- a/extras/wix.py
+++ b/waflib/extras/wix.py
diff --git a/extras/xcode6.py b/waflib/extras/xcode6.py
index 91bbff1..91bbff1 100644
--- a/extras/xcode6.py
+++ b/waflib/extras/xcode6.py
diff --git a/fixpy2.py b/waflib/fixpy2.py
index 24176e0..24176e0 100644
--- a/fixpy2.py
+++ b/waflib/fixpy2.py
diff --git a/processor.py b/waflib/processor.py
index 2eecf3b..2eecf3b 100755
--- a/processor.py
+++ b/waflib/processor.py
diff --git a/waflib/waf b/waflib/waf
new file mode 100755
index 0000000..e22930a
--- /dev/null
+++ b/waflib/waf
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+# Minimal waf script for projects that include waflib directly
+
+from waflib import Context, Scripting
+
+import inspect
+import os
+
+def main():
+ script_path = os.path.abspath(inspect.getfile(inspect.getmodule(main)))
+ project_path = os.path.dirname(script_path)
+ Scripting.waf_entry_point(os.getcwd(), Context.WAFVERSION, project_path)
+
+if __name__ == '__main__':
+ main()
diff --git a/wscript b/wscript
new file mode 100644
index 0000000..57ef231
--- /dev/null
+++ b/wscript
@@ -0,0 +1,506 @@
+#!/usr/bin/env python
+import os
+import shutil
+import subprocess
+import sys
+import waflib.Options as Options
+import waflib.extras.autowaf as autowaf
+import waflib.Build as Build
+import waflib.Logs as Logs
+
+# Library and package version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+LILV_VERSION = '0.24.5'
+LILV_MAJOR_VERSION = '0'
+
+# Mandatory waf variables
+APPNAME = 'lilv' # Package name for waf dist
+VERSION = LILV_VERSION # Package version for waf dist
+top = '.' # Source directory
+out = 'build' # Build directory
+
+test_plugins = [
+ 'bad_syntax',
+ 'failed_instantiation',
+ 'failed_lib_descriptor',
+ 'lib_descriptor',
+ 'missing_descriptor',
+ 'missing_name',
+ 'missing_plugin',
+ 'missing_port',
+ 'missing_port_name',
+ 'new_version',
+ 'old_version'
+]
+
+def options(ctx):
+ ctx.load('compiler_c')
+ ctx.load('compiler_cxx')
+ ctx.load('python')
+ autowaf.set_options(ctx, test=True)
+ opt = ctx.get_option_group('Configuration options')
+ 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('--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):
+ autowaf.display_header('Lilv Configuration')
+ conf.load('compiler_c', cache=True)
+ try:
+ conf.load('compiler_cxx', cache=True)
+ except:
+ pass
+
+ if Options.options.bindings:
+ try:
+ conf.load('python', cache=True)
+ conf.check_python_headers()
+ autowaf.define(conf, 'LILV_PYTHON', 1);
+ except:
+ Logs.warn('Failed to configure Python (%s)\n' % sys.exc_info()[1])
+
+ conf.load('autowaf', cache=True)
+ autowaf.set_c_lang(conf, 'c99')
+
+ conf.env.BASH_COMPLETION = not Options.options.no_bash_completion
+ 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.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD',
+ atleast_version='0.18.0', mandatory=True)
+ autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD',
+ atleast_version='0.14.0', mandatory=True)
+ autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM',
+ atleast_version='0.4.0', mandatory=True)
+ autowaf.check_pkg(conf, 'sndfile', uselib_store='SNDFILE',
+ atleast_version='1.0.0', mandatory=False)
+
+ defines = ['_POSIX_C_SOURCE=200809L', '_BSD_SOURCE', '_DEFAULT_SOURCE']
+ if conf.env.DEST_OS == 'darwin':
+ defines += ['_DARWIN_C_SOURCE']
+
+ rt_lib = ['rt']
+ if conf.env.DEST_OS == 'darwin' or conf.env.DEST_OS == 'win32':
+ rt_lib = []
+
+ autowaf.check_function(conf, 'c', 'lstat',
+ header_name = ['sys/stat.h'],
+ defines = defines,
+ define_name = 'HAVE_LSTAT',
+ mandatory = False)
+
+ autowaf.check_function(conf, 'c', 'flock',
+ header_name = 'sys/file.h',
+ defines = defines,
+ define_name = 'HAVE_FLOCK',
+ mandatory = False)
+
+ autowaf.check_function(conf, 'c', 'fileno',
+ header_name = 'stdio.h',
+ defines = defines,
+ define_name = 'HAVE_FILENO',
+ mandatory = False)
+
+ autowaf.check_function(conf, 'c', 'clock_gettime',
+ header_name = ['sys/time.h','time.h'],
+ defines = ['_POSIX_C_SOURCE=200809L'],
+ define_name = 'HAVE_CLOCK_GETTIME',
+ uselib_store = 'CLOCK_GETTIME',
+ lib = rt_lib,
+ mandatory = False)
+
+ conf.check_cc(define_name = 'HAVE_LIBDL',
+ lib = 'dl',
+ 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 conf.env.DEST_OS == '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 conf.env.DEST_OS == '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 conf.env.DEST_OS == 'haiku':
+ lv2_path = lilv_path_sep.join(['~/.lv2',
+ '/boot/common/add-ons/lv2'])
+ elif conf.env.DEST_OS == '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)
+
+ autowaf.set_lib_env(conf, 'lilv', LILV_VERSION)
+ conf.write_config_header('lilv_config.h', remove=False)
+
+ autowaf.display_summary(conf)
+ 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, libs=''):
+ obj = bld(features = 'c cprogram',
+ source = name + '.c',
+ includes = ['.', './src', './utils'],
+ use = 'liblilv',
+ target = name,
+ defines = defines,
+ install_path = '${BINDIR}')
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2 ' + libs)
+ 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'))
+
+ 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 = []
+ libflags = ['-fvisibility=hidden']
+ defines = []
+ if bld.is_defined('HAVE_LIBDL'):
+ lib += ['dl']
+ if bld.env.DEST_OS == 'win32':
+ lib = []
+ if bld.env.MSVC_COMPILER:
+ libflags = []
+
+ # 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',
+ 'LILV_PKG_LIBS' : ' -l'.join([''] + 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_VERSION,
+ install_path = '${LIBDIR}',
+ defines = ['LILV_SHARED', 'LILV_INTERNAL'],
+ cflags = libflags,
+ lib = lib)
+ autowaf.use_lib(bld, obj, 'SERD 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_VERSION,
+ install_path = '${LIBDIR}',
+ defines = defines + ['LILV_INTERNAL'])
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # Python bindings
+ if bld.is_defined('LILV_PYTHON'):
+ bld(features = 'subst',
+ is_copy = True,
+ source = 'bindings/python/lilv.py',
+ target = 'lilv.py',
+ install_path = '${PYTHONDIR}')
+
+ if bld.env.BUILD_TESTS:
+ import re
+
+ test_libs = lib
+ test_cflags = ['']
+ test_linkflags = ['']
+ if not bld.env.NO_COVERAGE:
+ test_cflags += ['--coverage']
+ test_linkflags += ['--coverage']
+
+ # Make a pattern for shared objects without the 'lib' prefix
+ module_pattern = re.sub('^lib', '', bld.env.cshlib_PATTERN)
+ shlib_ext = module_pattern[module_pattern.rfind('.'):]
+
+ for p in ['test'] + test_plugins:
+ obj = bld(features = 'c cshlib',
+ source = 'test/%s.lv2/%s.c' % (p, p),
+ name = p,
+ target = 'test/%s.lv2/%s' % (p, p),
+ install_path = None,
+ defines = defines,
+ cflags = test_cflags,
+ linkflags = test_linkflags,
+ lib = test_libs,
+ uselib = 'LV2')
+ obj.env.cshlib_PATTERN = module_pattern
+
+ for p in test_plugins:
+ if not bld.path.find_node('test/%s.lv2/test_%s.c' % (p, p)):
+ continue
+
+ obj = bld(features = 'c cprogram',
+ source = 'test/%s.lv2/test_%s.c' % (p, p),
+ target = 'test/test_%s' % p,
+ includes = ['.', './src'],
+ use = 'liblilv_profiled',
+ install_path = None,
+ defines = defines,
+ cflags = test_cflags,
+ linkflags = test_linkflags,
+ lib = test_libs,
+ uselib = 'LV2')
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # Test plugin data files
+ for p in ['test'] + test_plugins:
+ for i in [ 'manifest.ttl.in', p + '.ttl.in' ]:
+ bundle = 'test/%s.lv2/' % p
+ bld(features = 'subst',
+ source = bundle + i,
+ target = bundle + 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,
+ linkflags = test_linkflags,
+ lib = test_libs)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # Unit test program
+ testdir = os.path.abspath(autowaf.build_dir(APPNAME, 'test'))
+ bpath = os.path.join(testdir, 'test.lv2')
+ bpath = bpath.replace('\\', '/')
+ testdir = testdir.replace('\\', '/')
+ obj = bld(features = 'c cprogram',
+ source = 'test/lilv_test.c',
+ includes = ['.', './src'],
+ use = 'liblilv_profiled',
+ lib = test_libs,
+ target = 'test/lilv_test',
+ install_path = None,
+ defines = (defines + ['LILV_TEST_BUNDLE=\"%s/\"' % bpath] +
+ ['LILV_TEST_DIR=\"%s/\"' % testdir]),
+ cflags = test_cflags,
+ linkflags = test_linkflags)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ # C++ API test
+ if 'COMPILER_CXX' in bld.env:
+ obj = bld(features = 'cxx cxxprogram',
+ source = 'test/lilv_cxx_test.cpp',
+ includes = ['.', './src'],
+ use = 'liblilv_profiled',
+ lib = test_libs,
+ target = 'test/lilv_cxx_test',
+ install_path = None,
+ cxxflags = test_cflags,
+ linkflags = test_linkflags)
+ autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2')
+
+ if bld.is_defined('LILV_PYTHON'):
+ # Copy Python unittest files
+ for i in [ 'test_api.py' ]:
+ bld(features = 'subst',
+ is_copy = True,
+ source = 'bindings/test/python/' + i,
+ target = 'bindings/' + i,
+ install_path = None)
+
+ # Build bindings test plugin
+ obj = bld(features = 'c cshlib',
+ source = 'bindings/test/bindings_test_plugin.c',
+ name = 'bindings_test_plugin',
+ target = 'bindings/bindings_test_plugin.lv2/bindings_test_plugin',
+ install_path = None,
+ defines = defines,
+ cflags = test_cflags,
+ linkflags = test_linkflags,
+ lib = test_libs,
+ uselib = 'LV2')
+ obj.env.cshlib_PATTERN = module_pattern
+
+ # Bindings test plugin data files
+ for i in [ 'manifest.ttl.in', 'bindings_test_plugin.ttl.in' ]:
+ bld(features = 'subst',
+ source = 'bindings/test/' + i,
+ target = 'bindings/bindings_test_plugin.lv2/' + i.replace('.in', ''),
+ install_path = None,
+ SHLIB_EXT = shlib_ext)
+
+
+ # Utilities
+ if bld.env.BUILD_UTILS:
+ utils = '''
+ utils/lilv-bench
+ utils/lv2info
+ utils/lv2ls
+ '''
+ for i in utils.split():
+ build_util(bld, i, defines)
+
+ if bld.env.HAVE_SNDFILE:
+ obj = build_util(bld, 'utils/lv2apply', defines, 'SNDFILE')
+
+ # lv2bench (less portable than other utilities)
+ if bld.is_defined('HAVE_CLOCK_GETTIME') and not bld.env.STATIC_PROGS:
+ obj = build_util(bld, 'utils/lv2bench', defines)
+ if not bld.env.MSVC_COMPILER and not bld.env.DEST_OS == 'darwin':
+ 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')
+
+ 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):
+ import glob
+ os.system('rsync -ravz --delete -e ssh build/doc/html/ drobilla@drobilla.net:~/drobilla.net/docs/lilv/')
+ for page in glob.glob('doc/*.[1-8]'):
+ os.system('soelim %s | pre-grohtml troff -man -wall -Thtml | post-grohtml > build/%s.html' % (page, page))
+ os.system('rsync -avz --delete -e ssh build/%s.html drobilla@drobilla.net:~/drobilla.net/man/' % page)
+
+def test(ctx):
+ assert ctx.env.BUILD_TESTS, "You have run waf configure without the --test flag. No tests were run."
+ autowaf.pre_test(ctx, APPNAME)
+ if ctx.is_defined('LILV_PYTHON'):
+ os.environ['LD_LIBRARY_PATH'] = os.getcwd()
+ autowaf.run_tests(ctx, APPNAME, ['python -m unittest discover bindings/'])
+ os.environ['PATH'] = 'test' + os.pathsep + os.getenv('PATH')
+
+ Logs.pprint('GREEN', '')
+ autowaf.run_test(ctx, APPNAME, 'lilv_test', dirs=['./src','./test'], name='lilv_test')
+
+ for p in test_plugins:
+ test_prog = 'test_' + p + ' ' + ('test/%s.lv2/' % p)
+ if os.path.exists('test/test_' + p):
+ autowaf.run_test(ctx, APPNAME, test_prog, 0,
+ dirs=['./src','./test','./test/%s.lv2' % p])
+
+ autowaf.post_test(ctx, APPNAME)
+ try:
+ shutil.rmtree('state')
+ except:
+ pass
+
+def lint(ctx):
+ "checks code for style issues"
+ import subprocess
+ cmd = ("clang-tidy -p=. -header-filter=.* -checks=\"*," +
+ "-clang-analyzer-alpha.*," +
+ "-google-readability-todo," +
+ "-llvm-header-guard," +
+ "-llvm-include-order," +
+ "-misc-unused-parameters," +
+ "-readability-else-after-return\" " +
+ "$(find .. -name '*.c')")
+ subprocess.call(cmd, cwd='build', shell=True)
+
+def posts(ctx):
+ path = str(ctx.path.abspath())
+ autowaf.news_to_posts(
+ os.path.join(path, 'NEWS'),
+ {'title' : 'Lilv',
+ 'description' : autowaf.get_blurb(os.path.join(path, 'README')),
+ 'dist_pattern' : 'http://download.drobilla.net/lilv-%s.tar.bz2'},
+ { 'Author' : 'drobilla',
+ 'Tags' : 'Hacking, LAD, LV2, Lilv' },
+ os.path.join(out, 'posts'))