diff options
-rw-r--r-- | .gitattributes | 1 | ||||
-rw-r--r-- | AUTHORS | 11 | ||||
-rw-r--r-- | COPYING | 38 | ||||
-rw-r--r-- | INSTALL | 66 | ||||
-rw-r--r-- | NEWS | 228 | ||||
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | bindings/lilv.i | 66 | ||||
-rw-r--r-- | bindings/numpy.i | 1746 | ||||
-rw-r--r-- | bindings/python/Makefile | 186 | ||||
-rw-r--r-- | bindings/python/conf.py | 263 | ||||
-rw-r--r-- | bindings/python/index.rst | 9 | ||||
-rw-r--r-- | bindings/python/lilv.py | 1775 | ||||
-rwxr-xr-x | bindings/python/lv2_apply.py | 159 | ||||
-rwxr-xr-x | bindings/python/lv2_list.py | 9 | ||||
-rw-r--r-- | bindings/test/bindings_test_plugin.c | 196 | ||||
-rw-r--r-- | bindings/test/bindings_test_plugin.ttl.in | 62 | ||||
-rw-r--r-- | bindings/test/manifest.ttl.in | 7 | ||||
-rw-r--r-- | bindings/test/python/test_api.py | 290 | ||||
-rw-r--r-- | doc/layout.xml | 187 | ||||
-rw-r--r-- | doc/lv2apply.1 | 34 | ||||
-rw-r--r-- | doc/lv2info.1 | 33 | ||||
-rw-r--r-- | doc/lv2ls.1 | 30 | ||||
-rw-r--r-- | doc/reference.doxygen.in | 2415 | ||||
-rw-r--r-- | doc/style.css | 691 | ||||
-rw-r--r-- | lilv.pc.in | 11 | ||||
-rw-r--r-- | lilv.ttl | 29 | ||||
-rw-r--r-- | lilv/lilv.h | 1844 | ||||
-rw-r--r-- | lilv/lilvmm.hpp | 345 | ||||
-rw-r--r-- | src/collections.c | 224 | ||||
-rw-r--r-- | src/instance.c | 110 | ||||
-rw-r--r-- | src/lib.c | 112 | ||||
-rw-r--r-- | src/lilv_internal.h | 444 | ||||
-rw-r--r-- | src/node.c | 397 | ||||
-rw-r--r-- | src/plugin.c | 1138 | ||||
-rw-r--r-- | src/pluginclass.c | 88 | ||||
-rw-r--r-- | src/port.c | 268 | ||||
-rw-r--r-- | src/query.c | 139 | ||||
-rw-r--r-- | src/scalepoint.c | 49 | ||||
-rw-r--r-- | src/state.c | 1329 | ||||
-rw-r--r-- | src/ui.c | 111 | ||||
-rw-r--r-- | src/util.c | 640 | ||||
-rw-r--r-- | src/world.c | 1212 | ||||
-rw-r--r-- | src/zix/common.h | 88 | ||||
-rw-r--r-- | src/zix/tree.c | 716 | ||||
-rw-r--r-- | src/zix/tree.h | 148 | ||||
-rw-r--r-- | test/bad_syntax.lv2/bad_syntax.c | 93 | ||||
-rw-r--r-- | test/bad_syntax.lv2/bad_syntax.ttl.in | 22 | ||||
-rw-r--r-- | test/bad_syntax.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/bad_syntax.lv2/test_bad_syntax.c | 45 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/failed_instantiation.c | 70 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/failed_instantiation.ttl.in | 40 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/failed_instantiation.lv2/test_failed_instantiation.c | 45 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/failed_lib_descriptor.c | 30 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/failed_lib_descriptor.ttl.in | 38 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/failed_lib_descriptor.lv2/test_failed_lib_descriptor.c | 46 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/lib_descriptor.c | 112 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/lib_descriptor.ttl.in | 41 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/lib_descriptor.lv2/test_lib_descriptor.c | 59 | ||||
-rw-r--r-- | test/lilv_cxx_test.cpp | 23 | ||||
-rw-r--r-- | test/lilv_test.c | 2296 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/missing_descriptor.c | 21 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/missing_descriptor.ttl.in | 38 | ||||
-rw-r--r-- | test/missing_descriptor.lv2/test_missing_descriptor.c | 46 | ||||
-rw-r--r-- | test/missing_name.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_name.lv2/missing_name.c | 93 | ||||
-rw-r--r-- | test/missing_name.lv2/missing_name.ttl.in | 37 | ||||
-rw-r--r-- | test/missing_name.lv2/test_missing_name.c | 47 | ||||
-rw-r--r-- | test/missing_plugin.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_plugin.lv2/missing_plugin.c | 43 | ||||
-rw-r--r-- | test/missing_plugin.lv2/missing_plugin.ttl.in | 38 | ||||
-rw-r--r-- | test/missing_plugin.lv2/test_missing_plugin.c | 46 | ||||
-rw-r--r-- | test/missing_port.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_port.lv2/missing_port.c | 93 | ||||
-rw-r--r-- | test/missing_port.lv2/missing_port.ttl.in | 31 | ||||
-rw-r--r-- | test/missing_port.lv2/test_missing_port.c | 45 | ||||
-rw-r--r-- | test/missing_port_name.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/missing_port_name.lv2/missing_port_name.c | 93 | ||||
-rw-r--r-- | test/missing_port_name.lv2/missing_port_name.ttl.in | 30 | ||||
-rw-r--r-- | test/missing_port_name.lv2/test_missing_port_name.c | 49 | ||||
-rw-r--r-- | test/new_version.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/new_version.lv2/new_version.c | 93 | ||||
-rw-r--r-- | test/new_version.lv2/new_version.ttl.in | 40 | ||||
-rw-r--r-- | test/old_version.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/old_version.lv2/old_version.c | 93 | ||||
-rw-r--r-- | test/old_version.lv2/old_version.ttl.in | 40 | ||||
-rw-r--r-- | test/test.lv2/manifest.ttl.in | 7 | ||||
-rw-r--r-- | test/test.lv2/test.c | 394 | ||||
-rw-r--r-- | test/test.lv2/test.ttl.in | 46 | ||||
-rw-r--r-- | utils/bench.h | 52 | ||||
-rw-r--r-- | utils/lilv-bench.c | 38 | ||||
-rw-r--r-- | utils/lilv.bash_completion | 59 | ||||
-rw-r--r-- | utils/lv2apply.c | 354 | ||||
-rw-r--r-- | utils/lv2bench.c | 268 | ||||
-rw-r--r-- | utils/lv2info.c | 460 | ||||
-rw-r--r-- | utils/lv2ls.c | 93 | ||||
-rw-r--r-- | utils/uri_table.h | 73 | ||||
-rwxr-xr-x | waf | 171 | ||||
-rw-r--r-- | waflib/.gitignore (renamed from .gitignore) | 0 | ||||
-rw-r--r-- | waflib/Build.py (renamed from Build.py) | 0 | ||||
-rw-r--r-- | waflib/COPYING | 25 | ||||
-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-x | waflib/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-x | waflib/processor.py (renamed from processor.py) | 0 | ||||
-rwxr-xr-x | waflib/waf | 16 | ||||
-rw-r--r-- | wscript | 506 |
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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><http://example.org/foo ></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><plugin-uri> 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 ∈ + } 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 */ @@ -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}^�˫m�0� ��.�R�����w2��c[#0���uW�ܾ�����g���ʧm�٩��ݹWMU��}��ۂd�;��2#0�U���l�l4Td:�������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�#0v�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��A3BQ"�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,LQF��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�cFDkd�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,��Saiѥ@���~�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�?<YV�#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��篖��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�/@�!#Wh$�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�10��?�#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�0DO��?��=���\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��#0sw�\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�����TjTb�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�eCBK*��O�0/_F�ݿ�P���w�f�x#0a�Ѓ}�Ly��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�7SX�REn1?�f�1�S�K�vēZP�`� �x����<�U��y��5�M�&6#3�dS��(cm$v`�XU�51�TQ��X�A�6$E�vB�428Ү@��e���Z0Dp{RV�hU�x�/-4V����#W�Hpm���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�Dx�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��yZtuӿ�´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@�67d2Q�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�'�����\���?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����NJx|��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`oW����: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��{NN�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��uP𬵨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�"�&�EZ��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&��dE�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#WkS��: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 �#3Err���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��|/Ǵ$��;AQS�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�mD�!�[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`��VC�Ԇ��և��~�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(��+ȱ!�#3az/(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?�{Ata�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^�rN�� �\����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~�Ϟ���5wɞ*˪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!�/scEVY�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�wJo��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$,^b0����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��nd�#3u �!���d ��W���46�����Y�E#32+ϩM����s�����!�\��ukr�]�@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>���9lj5v�����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!bz�$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��44��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�CiM�;���!���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��xD�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�GvE�<pj�&��i)��|��1�)��PS(c�?;%gI��S?帐��IS���W�����1o�}�s�Ji�0��,X0R~L��BjG�dz:�+��'#�'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���|����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ш$�IN���ܧ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�woz���"`�ՠ�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���j1js�ڣ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�07mp�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ʩ���=z0��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{RMM�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#3wo�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�*�VI|�#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�����IC�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�FME��.+4W�av�;R����y"6��Bs�<>��m���۔,�pP��3q�^q�1�(�s��t9��96x��Ί�����K�(�Bv:;bN�ч��`�%��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��hMx<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�:� ����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Ǹ-�͔��4v#B�bP1oQ�C�`�Ѫ�YQ� �G��q�2�R�\r�ì)*ƕ�>�!���#3a�����r�ˆ�D�he�,dM@�=G��_#WN�#M�%&�-��FQ�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�3k��"!p�YV��$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���$�Elj9����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��!rG���������(��^�lU���&A�*��g�K) Hv%(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�������>��"(�@��|ޯ��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�B�p#0#0�6ض�����]�m�z�=�[����Qn�$ E *H�VC�a�a6Y({(xcF_��i��ֲQp��\���"�"���j5�$�ޔ1Z�M�Ǝ#W��辒l���)��)���j�A��hJ~<�mǴPdbE<� ���{Yë��][{�Lcؚ�R��P���`}��W��3��μ�I.w���r��v7((/4nb��ם���^{<�u�,h���KE�B�#W_�ve���e�F6���H�-s@�ZxL�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�*Fs�^�n�T�{Bzn� �;���h�;�_�&�Rx�/����g� E�~g�I��0fB�+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?45t9w�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�Yb���% 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 첥��#3xFM����[�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/ƪ���1Q��� ϙ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;�Xk�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���Nd�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���-$bTm1��Ʃ�]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���BJ����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�WWbQ�����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ϕwQL7#W��!���L�p$?�F͇�TFphЂ�,�`�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ϓ�~jY�x����Vv:m����we��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�6j��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��LeLw邒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�0y: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���>���qnQBD}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\�c2���*�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#W3�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��czphI�#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�$ƃFlA�"h(�b�"�ʈ� �T!@��v�_ �TjR ?��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�ڀ�����;l1E.=~��#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)^<#W1#�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=6uIƴ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,��&�Il�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�����nFW6�*�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�8du1p+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��Y0Lm��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��35� �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@��3Rh;#3u�B6h"Ы�D���ml�3��W n�mi��#_x�j#31�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�Ez��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�";�{&ZHXr�#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�vwwHra߅�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"Jb"+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���Yr�|Ĺ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/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/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() @@ -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')) |