| @@ -1 +1 @@ | |||
| ../modules/lilv/lilv-0.22.1/lilv | |||
| ../modules/lilv/lilv-0.24.0/lilv | |||
| @@ -1 +1 @@ | |||
| ../modules/lilv/serd-0.22.0/serd/ | |||
| ../modules/lilv/serd-0.24.0/serd/ | |||
| @@ -1 +1 @@ | |||
| ../modules/lilv/sord-0.14.0/sord/ | |||
| ../modules/lilv/sord-0.16.0/sord/ | |||
| @@ -1 +1 @@ | |||
| ../modules/lilv/sratom-0.4.7/sratom/ | |||
| ../modules/lilv/sratom-0.6.0/sratom/ | |||
| @@ -10,10 +10,10 @@ include ../Makefile.mk | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| SERD_VERSION = 0.22.0 | |||
| SORD_VERSION = 0.14.0 | |||
| SRATOM_VERSION = 0.4.7 | |||
| LILV_VERSION = 0.22.1 | |||
| SERD_VERSION = 0.24.0 | |||
| SORD_VERSION = 0.16.0 | |||
| SRATOM_VERSION = 0.6.0 | |||
| LILV_VERSION = 0.24.0 | |||
| # ---------------------------------------------------------------------------------------------------------------------------- | |||
| @@ -1,11 +0,0 @@ | |||
| Lilv | |||
| ---- | |||
| Lilv is a library for LV2 hosts intended to make using LV2 Plugins as simple | |||
| as possible (without sacrificing capabilities). | |||
| More information about LV2 plugins can be found at <http://lv2plug.in>. | |||
| More information about Lilv can be found at <http://drobilla.net/software/lilv>. | |||
| -- David Robillard <d@drobilla.net> | |||
| @@ -1,84 +0,0 @@ | |||
| # 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. | |||
| from lilv import * | |||
| import unittest | |||
| import os | |||
| class UriTests(unittest.TestCase): | |||
| def setUp(self): | |||
| self.world = lilv_world_new() | |||
| lilv_world_load_all(self.world) | |||
| def testInvalidURI(self): | |||
| self.uri = lilv_new_uri(self.world, "invalid_uri") | |||
| self.assertIsNone(self.uri) | |||
| def testInvalidURI2(self): | |||
| self.uri = lilv_new_uri(self.world, "invalid_uri") | |||
| self.assertFalse( lilv_node_is_uri(self.uri) ) | |||
| def testNonExistentURI(self): | |||
| self.uri = lilv_new_uri(self.world, "exist:does_not") | |||
| plugins = lilv_world_get_all_plugins(self.world) | |||
| self.plugin = lilv_plugins_get_by_uri(plugins, self.uri) | |||
| self.assertIsNone(self.plugin) | |||
| def testPortTypes(self): | |||
| self.uri = lilv_new_uri(self.world, LILV_URI_INPUT_PORT) | |||
| self.assertIsNotNone(self.uri) | |||
| def testPortTypes2(self): | |||
| self.uri = lilv_new_uri(self.world, LILV_URI_OUTPUT_PORT) | |||
| self.assertIsNotNone(self.uri) | |||
| def testPortTypes3(self): | |||
| self.uri = lilv_new_uri(self.world, LILV_URI_AUDIO_PORT) | |||
| self.assertIsNotNone(self.uri) | |||
| def testPortTypes4(self): | |||
| self.uri = lilv_new_uri(self.world, LILV_URI_CONTROL_PORT) | |||
| self.assertIsNotNone(self.uri) | |||
| def tearDown(self): | |||
| lilv_node_free(self.uri) | |||
| lilv_world_free(self.world) | |||
| class PluginTests(unittest.TestCase): | |||
| def setUp(self): | |||
| self.world = lilv_world_new() | |||
| location = "file://" + os.getcwd() + "/bindings/bindings_test_plugin.lv2/" | |||
| self.plugin_uri = lilv_new_uri(self.world, location) | |||
| self.assertIsNotNone(self.plugin_uri, "Invalid URI: '" + location + "'") | |||
| lilv_world_load_bundle(self.world, self.plugin_uri) | |||
| self.plugins = lilv_world_get_all_plugins(self.world) | |||
| self.plugin = lilv_plugins_get(self.plugins, lilv_plugins_begin(self.plugins)) | |||
| self.assertIsNotNone(self.plugin, msg="Test plugin not found at location: '" + location + "'") | |||
| self.assertEqual(location, lilv_node_as_string(lilv_plugin_get_bundle_uri(self.plugin))) | |||
| self.instance = lilv_plugin_instantiate(self.plugin, 48000, None) | |||
| self.assertIsNotNone(self.instance) | |||
| self.lv2_InputPort = lilv_new_uri(self.world, LILV_URI_INPUT_PORT) | |||
| self.lv2_OutputPort = lilv_new_uri(self.world, LILV_URI_OUTPUT_PORT) | |||
| self.lv2_AudioPort = lilv_new_uri(self.world, LILV_URI_AUDIO_PORT) | |||
| self.lv2_ControlPort = lilv_new_uri(self.world, LILV_URI_CONTROL_PORT) | |||
| def testPorts(self): | |||
| n = lilv_plugin_get_num_ports_of_class(self.plugin, self.lv2_InputPort, self.lv2_AudioPort) | |||
| self.assertEqual(n, 1) | |||
| def testPorts2(self): | |||
| n = lilv_plugin_get_num_ports_of_class(self.plugin, self.lv2_OutputPort, self.lv2_AudioPort) | |||
| self.assertEqual(n, 1) | |||
| def testPorts3(self): | |||
| n = lilv_plugin_get_num_ports_of_class(self.plugin, self.lv2_OutputPort, self.lv2_ControlPort) | |||
| self.assertEqual(n, 1) | |||
| def testPorts4(self): | |||
| n = lilv_plugin_get_num_ports_of_class(self.plugin, self.lv2_InputPort, self.lv2_ControlPort) | |||
| self.assertEqual(n, 1) | |||
| def tearDown(self): | |||
| lilv_node_free(self.lv2_InputPort) | |||
| lilv_node_free(self.lv2_OutputPort) | |||
| lilv_node_free(self.lv2_AudioPort) | |||
| lilv_node_free(self.plugin_uri) | |||
| lilv_world_free(self.world) | |||
| @@ -1,70 +0,0 @@ | |||
| # 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 | |||
| 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.assertEqual(self.plugin_uri, None) | |||
| def testInvalidURI2(self): | |||
| self.plugin_uri = self.world.new_uri("invalid_uri") | |||
| self.assertFalse( lilv.lilv_node_is_uri(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 PluginTests(unittest.TestCase): | |||
| def setUp(self): | |||
| self.world = lilv.World() | |||
| location = "file://" + os.getcwd() + "/bindings/bindings_test_plugin.lv2/" | |||
| self.plugin_uri = self.world.new_uri(location) | |||
| self.assertIsNotNone(self.plugin_uri, "Invalid URI: '" + location + "'") | |||
| self.world.load_bundle(self.plugin_uri) | |||
| self.plugins = self.world.get_all_plugins() | |||
| self.plugin = self.plugins.get(self.plugins.begin()) | |||
| self.assertIsNotNone(self.plugin, msg="Test plugin not found at location: '" + location + "'") | |||
| self.assertEqual(location, self.plugin.get_bundle_uri().as_string()) | |||
| 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 testPorts(self): | |||
| n = self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_AudioPort) | |||
| self.assertEqual(n, 1) | |||
| def testPorts2(self): | |||
| n = self.plugin.get_num_ports_of_class(self.lv2_OutputPort, self.lv2_AudioPort) | |||
| self.assertEqual(n, 1) | |||
| def testPorts3(self): | |||
| n = self.plugin.get_num_ports_of_class(self.lv2_OutputPort, self.lv2_ControlPort) | |||
| self.assertEqual(n, 1) | |||
| def testPorts4(self): | |||
| n = self.plugin.get_num_ports_of_class(self.lv2_InputPort, self.lv2_ControlPort) | |||
| self.assertEqual(n, 1) | |||
| @@ -1,4 +1,4 @@ | |||
| Copyright 2011-2015 David Robillard <http://drobilla.net> | |||
| Copyright 2011-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 | |||
| @@ -1,8 +1,31 @@ | |||
| lilv (0.22.1) unstable; | |||
| * Fix Python bindings | |||
| -- David Robillard <d@drobilla.net> Fri, 09 Oct 2015 13:01:14 -0400 | |||
| lilv (0.24.1) unstable; | |||
| * Fix comparison of restored states with paths | |||
| -- David Robillard <d@drobilla.net> Mon, 26 Sep 2016 12:51:37 -0400 | |||
| 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; | |||
| @@ -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> | |||
| @@ -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." | |||
| @@ -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.0' | |||
| # The full version, including alpha/beta/rc tags. | |||
| release = '0.24.0' | |||
| # 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 | |||
| @@ -0,0 +1,9 @@ | |||
| Lilv Python Documentation | |||
| ========================= | |||
| .. toctree:: | |||
| .. automodule:: lilv | |||
| :noindex: | |||
| :members: | |||
| @@ -41,8 +41,8 @@ class WavFile(object): | |||
| data = [(i - float(range/2)) / float(range/2) for i in data] | |||
| channels = [] | |||
| for i in xrange(self.nchannels): | |||
| channels.append([data[j] for j in xrange(0, len(data), self.nchannels) ]) | |||
| for i in range(self.nchannels): | |||
| channels.append([data[j] for j in range(0, len(data), self.nchannels) ]) | |||
| return channels | |||
| @@ -57,6 +57,7 @@ def main(): | |||
| # Initialise Lilv | |||
| world = lilv.World() | |||
| ns = world.ns | |||
| world.load_all() | |||
| plugin_uri = sys.argv[1] | |||
| @@ -65,19 +66,14 @@ def main(): | |||
| # Find plugin | |||
| plugin_uri_node = world.new_uri(plugin_uri) | |||
| plugin = world.get_all_plugins().get_by_uri(plugin_uri_node) | |||
| if not plugin: | |||
| print("Unknown plugin `%s'\n" % plugin_uri) | |||
| plugins = world.get_all_plugins() | |||
| if plugin_uri_node not in plugins: | |||
| print("Unknown plugin `%s'" % plugin_uri) | |||
| sys.exit(1) | |||
| lv2_InputPort = world.new_uri(lilv.LILV_URI_INPUT_PORT) | |||
| lv2_OutputPort = world.new_uri(lilv.LILV_URI_OUTPUT_PORT) | |||
| lv2_AudioPort = world.new_uri(lilv.LILV_URI_AUDIO_PORT) | |||
| lv2_ControlPort = world.new_uri(lilv.LILV_URI_CONTROL_PORT) | |||
| lv2_default = world.new_uri("http://lv2plug.in/ns/lv2core#default") | |||
| n_audio_in = plugin.get_num_ports_of_class(lv2_InputPort, lv2_AudioPort) | |||
| n_audio_out = plugin.get_num_ports_of_class(lv2_OutputPort, lv2_AudioPort) | |||
| 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) | |||
| @@ -120,22 +116,21 @@ def main(): | |||
| control_output_buffers = [] | |||
| for index in range(plugin.get_num_ports()): | |||
| port = plugin.get_port_by_index(index) | |||
| if port.is_a(lv2_InputPort): | |||
| if port.is_a(lv2_AudioPort): | |||
| 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(lv2_ControlPort): | |||
| #if port.has_property(lv2_default): # Doesn't seem to work | |||
| default = lilv.lilv_node_as_float(lilv.lilv_nodes_get_first(port.get_value(lv2_default))) | |||
| 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(lv2_OutputPort): | |||
| if port.is_a(lv2_AudioPort): | |||
| 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(lv2_ControlPort): | |||
| 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: | |||
| @@ -156,7 +151,7 @@ def main(): | |||
| # 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)) | |||
| wav_out.writeframes(wave.struct.pack("%u%s" % (len(chunk), wav_in.struct_fmt_code), *chunk.astype(int))) | |||
| wav_out.close() | |||
| @@ -54,7 +54,7 @@ typedef enum { | |||
| every instance method. In this simple plugin, only port buffers need to be | |||
| stored, since there is no additional instance data. */ | |||
| typedef struct { | |||
| // Port buffers | |||
| float* buf; | |||
| } Test; | |||
| /** | |||
| @@ -179,7 +179,7 @@ static const LV2_Descriptor descriptor = { | |||
| 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. | |||
| */ | |||
| @@ -16,18 +16,27 @@ | |||
| @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: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 ; | |||
| @@ -47,3 +56,7 @@ | |||
| lv2:symbol "audio_output" ; | |||
| lv2:name "Audio Output" ; | |||
| ] . | |||
| <http://example.org/lilv-bindings-test-plugin-ui> | |||
| a ui:GtkUI ; | |||
| ui:binary <TODO> . | |||
| @@ -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) | |||
| @@ -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> | |||
| @@ -1,17 +1,17 @@ | |||
| .TH LV2INFO 1 "8 Jan 2012" | |||
| .TH LV2INFO 1 "05 Sep 2016" | |||
| .SH NAME | |||
| .B lv2info \- print information about an installed LV2 plugin. | |||
| .B lv2info \- print information about an installed LV2 plugin | |||
| .SH SYNOPSIS | |||
| .B lv2info PLUGIN_URI | |||
| .SH OPTIONS | |||
| .TP | |||
| \fB\-p FILE | |||
| \fB\-p FILE\fR | |||
| Write Turtle description of plugin to FILE | |||
| .TP | |||
| \fB\-m FILE | |||
| \fB\-m FILE\fR | |||
| Add record of plugin to manifest FILE | |||
| .TP | |||
| @@ -22,7 +22,7 @@ Display help and exit | |||
| \fB\-\-version\fR | |||
| Display version information and exit | |||
| .SH SEE ALSO | |||
| .SH "SEE ALSO" | |||
| .BR lilv(3), | |||
| .BR lv2ls(1) | |||
| @@ -1,7 +1,7 @@ | |||
| .TH LV2LS 1 "17 Jan 2012" | |||
| .TH LV2LS 1 "26 Aug 2016" | |||
| .SH NAME | |||
| .B lv2ls \- List all installed LV2 plugins. | |||
| .B lv2ls \- list all installed LV2 plugins | |||
| .SH SYNOPSIS | |||
| .B lv2ls [OPTION]... | |||
| @@ -19,7 +19,7 @@ Display help and exit | |||
| \fB\-\-version\fR | |||
| Display version information and exit | |||
| .SH SEE ALSO | |||
| .SH "SEE ALSO" | |||
| .BR lilv(3), | |||
| .BR lv2info(1) | |||
| @@ -1,4 +1,4 @@ | |||
| # Doxyfile 1.8.7 | |||
| # Doxyfile 1.8.12 | |||
| # This file describes the settings to be used by the documentation system | |||
| # doxygen (www.doxygen.org) for a project. | |||
| @@ -46,10 +46,10 @@ PROJECT_NUMBER = @LILV_VERSION@ | |||
| PROJECT_BRIEF = | |||
| # With the PROJECT_LOGO tag one can specify an logo or 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. | |||
| # 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 = | |||
| @@ -60,7 +60,7 @@ PROJECT_LOGO = | |||
| OUTPUT_DIRECTORY = . | |||
| # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- | |||
| # 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 | |||
| @@ -93,14 +93,14 @@ ALLOW_UNICODE_NAMES = NO | |||
| OUTPUT_LANGUAGE = English | |||
| # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member | |||
| # 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 | |||
| # 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 | |||
| @@ -135,7 +135,7 @@ ALWAYS_DETAILED_SEC = NO | |||
| INLINE_INHERITED_MEMB = NO | |||
| # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path | |||
| # 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. | |||
| @@ -205,9 +205,9 @@ MULTILINE_CPP_IS_BRIEF = NO | |||
| 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. | |||
| # 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 | |||
| @@ -276,7 +276,7 @@ OPTIMIZE_OUTPUT_VHDL = NO | |||
| # 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: 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. | |||
| @@ -293,10 +293,19 @@ EXTENSION_MAPPING = | |||
| 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 by putting a % sign in front of the word | |||
| # or globally by setting AUTOLINK_SUPPORT to NO. | |||
| # 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 | |||
| @@ -336,13 +345,20 @@ SIP_SUPPORT = NO | |||
| 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 | |||
| # 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 | |||
| @@ -401,7 +417,7 @@ LOOKUP_CACHE_SIZE = 0 | |||
| # Build related configuration options | |||
| #--------------------------------------------------------------------------- | |||
| # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in | |||
| # 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. | |||
| @@ -411,35 +427,35 @@ LOOKUP_CACHE_SIZE = 0 | |||
| EXTRACT_ALL = YES | |||
| # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will | |||
| # 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 | |||
| # 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 | |||
| # 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 | |||
| # 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. When set to YES local methods, | |||
| # 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 in the documentation. If set to NO, only methods in the interface are | |||
| # included. | |||
| # The default value is: NO. | |||
| @@ -464,21 +480,21 @@ 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. | |||
| # 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 | |||
| # (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 | |||
| # 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. | |||
| @@ -492,7 +508,7 @@ HIDE_IN_BODY_DOCS = YES | |||
| 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 | |||
| # 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. | |||
| @@ -501,12 +517,19 @@ INTERNAL_DOCS = NO | |||
| 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 | |||
| # 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. | |||
| @@ -534,14 +557,14 @@ 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. | |||
| # 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 | |||
| # 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. | |||
| @@ -586,27 +609,25 @@ SORT_BY_SCOPE_NAME = YES | |||
| 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 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 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 | |||
| # 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 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. | |||
| @@ -631,8 +652,8 @@ ENABLED_SECTIONS = | |||
| 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 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 | |||
| @@ -680,8 +701,7 @@ LAYOUT_FILE = @LILV_SRCDIR@/doc/layout.xml | |||
| # 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. Do not use file names with spaces, bibtex cannot handle them. See | |||
| # also \cite for info how to create references. | |||
| # search path. See also \cite for info how to create references. | |||
| CITE_BIB_FILES = | |||
| @@ -697,7 +717,7 @@ CITE_BIB_FILES = | |||
| 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 | |||
| # 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. | |||
| @@ -705,7 +725,7 @@ QUIET = YES | |||
| WARNINGS = YES | |||
| # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate | |||
| # 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. | |||
| @@ -722,12 +742,18 @@ 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. | |||
| # 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 | |||
| @@ -751,7 +777,7 @@ WARN_LOGFILE = | |||
| # 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. | |||
| # 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 | |||
| @@ -767,12 +793,17 @@ 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. 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, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, | |||
| # *.qsf, *.as and *.js. | |||
| # *.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 = | |||
| @@ -858,6 +889,10 @@ IMAGE_PATH = | |||
| # 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 = | |||
| @@ -867,11 +902,15 @@ INPUT_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 | |||
| # 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. | |||
| @@ -931,7 +970,7 @@ REFERENCED_BY_RELATION = YES | |||
| 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 | |||
| # 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. | |||
| @@ -1008,7 +1047,7 @@ IGNORE_PREFIX = | |||
| # Configuration options related to the HTML output | |||
| #--------------------------------------------------------------------------- | |||
| # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output | |||
| # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output | |||
| # The default value is: YES. | |||
| GENERATE_HTML = YES | |||
| @@ -1070,13 +1109,15 @@ HTML_FOOTER = | |||
| HTML_STYLESHEET = @LILV_SRCDIR@/doc/style.css | |||
| # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- | |||
| # defined cascading style sheet that is included after the standard style sheets | |||
| # 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 therefor more robust against future updates. | |||
| # Doxygen will copy the style sheet file to the output directory. For an example | |||
| # see the documentation. | |||
| # 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 = | |||
| @@ -1092,7 +1133,7 @@ HTML_EXTRA_STYLESHEET = | |||
| HTML_EXTRA_FILES = | |||
| # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen | |||
| # will adjust the colors in the stylesheet and background images according to | |||
| # 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 | |||
| @@ -1123,8 +1164,9 @@ 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 NO can help when comparing the output of multiple runs. | |||
| # The default value is: YES. | |||
| # 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 | |||
| @@ -1220,28 +1262,28 @@ GENERATE_HTMLHELP = NO | |||
| 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 | |||
| # 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 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) | |||
| # 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 | |||
| # 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. | |||
| @@ -1355,7 +1397,7 @@ DISABLE_INDEX = YES | |||
| # 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 stylesheets (see HTML_EXTRA_STYLESHEET) one can | |||
| # 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 | |||
| @@ -1374,7 +1416,7 @@ GENERATE_TREEVIEW = NO | |||
| # 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 = 4 | |||
| 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. | |||
| @@ -1383,7 +1425,7 @@ ENUM_VALUES_PER_LINE = 4 | |||
| TREEVIEW_WIDTH = 250 | |||
| # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to | |||
| # 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. | |||
| @@ -1412,7 +1454,7 @@ 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 prerendered bitmaps. Use this if you do not have LaTeX | |||
| # 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. | |||
| @@ -1498,7 +1540,7 @@ SERVER_BASED_SEARCH = NO | |||
| # 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 | |||
| # 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/). | |||
| # | |||
| @@ -1511,7 +1553,7 @@ 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 | |||
| # 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. | |||
| @@ -1549,7 +1591,7 @@ EXTRA_SEARCH_MAPPINGS = | |||
| # Configuration options related to the LaTeX output | |||
| #--------------------------------------------------------------------------- | |||
| # If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. | |||
| # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. | |||
| # The default value is: YES. | |||
| GENERATE_LATEX = NO | |||
| @@ -1580,7 +1622,7 @@ LATEX_CMD_NAME = latex | |||
| MAKEINDEX_CMD_NAME = makeindex | |||
| # If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX | |||
| # 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. | |||
| @@ -1598,9 +1640,12 @@ COMPACT_LATEX = NO | |||
| 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. To get the times font for | |||
| # instance you can specify | |||
| # EXTRA_PACKAGES=times | |||
| # 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. | |||
| @@ -1614,23 +1659,36 @@ EXTRA_PACKAGES = | |||
| # | |||
| # 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. Doxygen will | |||
| # replace them by respectively the title of the page, the current date and time, | |||
| # only the current date, the version number of doxygen, the project name (see | |||
| # PROJECT_NAME), or the project number (see PROJECT_NUMBER). | |||
| # $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. | |||
| # 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 | |||
| @@ -1646,15 +1704,15 @@ LATEX_EXTRA_FILES = | |||
| # The default value is: YES. | |||
| # This tag requires that the tag GENERATE_LATEX is set to YES. | |||
| PDF_HYPERLINKS = NO | |||
| PDF_HYPERLINKS = YES | |||
| # If the LATEX_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 | |||
| # 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 = NO | |||
| 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 | |||
| @@ -1690,11 +1748,19 @@ LATEX_SOURCE_CODE = NO | |||
| 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 | |||
| # 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. | |||
| @@ -1709,7 +1775,7 @@ GENERATE_RTF = NO | |||
| RTF_OUTPUT = rtf | |||
| # If the COMPACT_RTF tag is set to YES doxygen generates more compact 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. | |||
| @@ -1746,11 +1812,21 @@ RTF_STYLESHEET_FILE = | |||
| 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 | |||
| # If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for | |||
| # classes and files. | |||
| # The default value is: NO. | |||
| @@ -1794,7 +1870,7 @@ 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 | |||
| # 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. | |||
| @@ -1808,7 +1884,7 @@ GENERATE_XML = NO | |||
| XML_OUTPUT = xml | |||
| # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program | |||
| # 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. | |||
| @@ -1821,7 +1897,7 @@ XML_PROGRAMLISTING = YES | |||
| # Configuration options related to the DOCBOOK output | |||
| #--------------------------------------------------------------------------- | |||
| # If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files | |||
| # 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. | |||
| @@ -1835,14 +1911,23 @@ GENERATE_DOCBOOK = NO | |||
| 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. | |||
| # 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 | |||
| @@ -1851,7 +1936,7 @@ 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 | |||
| # 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. | |||
| @@ -1859,7 +1944,7 @@ GENERATE_AUTOGEN_DEF = NO | |||
| GENERATE_PERLMOD = NO | |||
| # If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary | |||
| # 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. | |||
| @@ -1867,9 +1952,9 @@ GENERATE_PERLMOD = NO | |||
| PERLMOD_LATEX = NO | |||
| # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely | |||
| # 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 | |||
| # 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. | |||
| @@ -1889,14 +1974,14 @@ PERLMOD_MAKEVAR_PREFIX = | |||
| # Configuration options related to the preprocessor | |||
| #--------------------------------------------------------------------------- | |||
| # If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all | |||
| # 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 | |||
| # 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. | |||
| @@ -1912,7 +1997,7 @@ MACRO_EXPANSION = NO | |||
| EXPAND_ONLY_PREDEF = NO | |||
| # If the SEARCH_INCLUDES tag is set to YES the includes files in the | |||
| # 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. | |||
| @@ -1988,20 +2073,21 @@ TAGFILES = | |||
| 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. | |||
| # 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 | |||
| # 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 | |||
| # 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. | |||
| @@ -2018,7 +2104,7 @@ 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 | |||
| # 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 | |||
| @@ -2043,7 +2129,7 @@ MSCGEN_PATH = | |||
| DIA_PATH = | |||
| # If set to YES, the inheritance and collaboration graphs will hide inheritance | |||
| # 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. | |||
| @@ -2054,7 +2140,7 @@ HIDE_UNDOC_RELATIONS = YES | |||
| # 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: YES. | |||
| # The default value is: NO. | |||
| HAVE_DOT = NO | |||
| @@ -2068,7 +2154,7 @@ HAVE_DOT = NO | |||
| DOT_NUM_THREADS = 0 | |||
| # When you want a differently looking font n the dot files that doxygen | |||
| # 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 | |||
| @@ -2116,7 +2202,7 @@ COLLABORATION_GRAPH = NO | |||
| GROUP_GRAPHS = YES | |||
| # If the UML_LOOK tag is set to YES doxygen will generate inheritance and | |||
| # 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. | |||
| @@ -2168,7 +2254,8 @@ INCLUDED_BY_GRAPH = NO | |||
| # | |||
| # 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. | |||
| # 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. | |||
| @@ -2179,7 +2266,8 @@ CALL_GRAPH = NO | |||
| # | |||
| # 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. | |||
| # 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. | |||
| @@ -2202,13 +2290,15 @@ GRAPHICAL_HIERARCHY = NO | |||
| DIRECTORY_GRAPH = NO | |||
| # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images | |||
| # generated by dot. | |||
| # 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, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, | |||
| # png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, | |||
| # gif:cairo:gd, gif:gd, gif:gd:gd and svg. | |||
| # 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. | |||
| @@ -2251,6 +2341,19 @@ MSCFILE_DIRS = | |||
| 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 | |||
| @@ -2287,7 +2390,7 @@ MAX_DOT_GRAPH_DEPTH = 0 | |||
| DOT_TRANSPARENT = NO | |||
| # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output | |||
| # 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. | |||
| @@ -2304,7 +2407,7 @@ DOT_MULTI_TARGETS = NO | |||
| GENERATE_LEGEND = NO | |||
| # If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot | |||
| # 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. | |||
| @@ -1,69 +1,54 @@ | |||
| body { | |||
| font-size: medium; | |||
| font-family: sans-serif; | |||
| } | |||
| #top { | |||
| background-color: #F3F3F3; | |||
| max-width: 80em; | |||
| margin: 0; | |||
| padding: 0; | |||
| border-bottom: 1px solid #DDD; | |||
| margin-bottom: 1ex; | |||
| font-size: xx-large; | |||
| font-weight: bold; | |||
| } | |||
| div.header { | |||
| display: none; | |||
| margin-left: auto; | |||
| margin-right: auto; | |||
| background: #FFF; | |||
| color: #000; | |||
| } | |||
| .tabs { | |||
| #titlearea { | |||
| display: none; | |||
| } | |||
| h1 h2 h3 h4 h5 h6 { | |||
| font-weight: bold; | |||
| } | |||
| h1 { | |||
| font-size: 164%; | |||
| font-size: 180%; | |||
| font-weight: 900; | |||
| } | |||
| h2 { | |||
| font-size: 132%; | |||
| font-size: 140%; | |||
| font-weight: 700; | |||
| } | |||
| h3 { | |||
| font-size: 124%; | |||
| font-size: 120%; | |||
| font-weight: 700; | |||
| } | |||
| h4 { | |||
| font-size: 116%; | |||
| font-size: 110%; | |||
| font-weight: 700; | |||
| } | |||
| h5 { | |||
| font-size: 108%; | |||
| font-size: 100%; | |||
| font-weight: 700; | |||
| } | |||
| h6 { | |||
| font-size: 100%; | |||
| font-weight: 600; | |||
| } | |||
| p { | |||
| margin: 0 0 1ex 0; | |||
| } | |||
| br { | |||
| display: none; | |||
| margin: 0 0 1em 0; | |||
| } | |||
| dt { | |||
| font-weight: 700; | |||
| } | |||
| div.multicol { | |||
| } | |||
| p.startli,p.startdd,p.starttd { | |||
| margin-top: 2px; | |||
| } | |||
| @@ -85,12 +70,12 @@ caption { | |||
| } | |||
| span.legend { | |||
| font-size: 70%; | |||
| font-size: small; | |||
| text-align: center; | |||
| } | |||
| h3.version { | |||
| font-size: 90%; | |||
| font-size: small; | |||
| text-align: center; | |||
| } | |||
| @@ -102,23 +87,18 @@ div.qindex,div.navtab { | |||
| padding: 2px; | |||
| } | |||
| div.qindex,div.navpath { | |||
| width: 100%; | |||
| line-height: 140%; | |||
| } | |||
| div.navtab { | |||
| margin-right: 15px; | |||
| } | |||
| /* @group Link Styling */ | |||
| a { | |||
| color: #3D8C57; | |||
| color: #546E00; | |||
| text-decoration: none; | |||
| } | |||
| .contents a:visited { | |||
| color: #50755E; | |||
| color: #344E00; | |||
| } | |||
| a:hover { | |||
| @@ -131,6 +111,10 @@ a.qindexHL { | |||
| border: 1px double #869DCA; | |||
| } | |||
| code { | |||
| color: #444; | |||
| } | |||
| a.code { | |||
| color: #4665A2; | |||
| } | |||
| @@ -146,7 +130,6 @@ dl.el { | |||
| .fragment { | |||
| font-family: monospace, fixed; | |||
| font-size: 105%; | |||
| } | |||
| pre.fragment { | |||
| @@ -155,7 +138,6 @@ pre.fragment { | |||
| padding: 4px 6px; | |||
| margin: 4px 8px 4px 2px; | |||
| overflow: auto; | |||
| font-size: 9pt; | |||
| line-height: 125%; | |||
| } | |||
| @@ -176,17 +158,15 @@ div.groupHeader { | |||
| font-weight: 700; | |||
| } | |||
| a + h2.groupheader { | |||
| display: none; | |||
| } | |||
| div.groupText { | |||
| margin-left: 16px; | |||
| font-style: italic; | |||
| } | |||
| body { | |||
| background: #FFF; | |||
| color: #000; | |||
| margin: 0; | |||
| } | |||
| div.contents { | |||
| margin-top: 10px; | |||
| margin-left: 10px; | |||
| @@ -216,9 +196,6 @@ p.formulaDsp { | |||
| text-align: center; | |||
| } | |||
| img.formulaDsp { | |||
| } | |||
| img.formulaInl { | |||
| vertical-align: middle; | |||
| } | |||
| @@ -236,7 +213,7 @@ div.center img { | |||
| address.footer { | |||
| text-align: right; | |||
| padding: 0 0.25em 0.25em 0; | |||
| padding-right: 12px; | |||
| } | |||
| img.footer { | |||
| @@ -250,7 +227,7 @@ span.keyword { | |||
| } | |||
| span.keywordtype { | |||
| color: #604020; | |||
| color: #3E873E; | |||
| } | |||
| span.keywordflow { | |||
| @@ -287,7 +264,7 @@ span.vhdllogic { | |||
| /* @end */ | |||
| td.tiny { | |||
| font-size: 75%; | |||
| font-size: x-small; | |||
| } | |||
| .dirtab { | |||
| @@ -314,14 +291,16 @@ hr.footer { | |||
| /* @group Member Descriptions */ | |||
| table.memberdecls { | |||
| border-spacing: 0; | |||
| font-size: small; | |||
| border-spacing: 0.125em; | |||
| } | |||
| h2.groupheader { | |||
| margin: 0.5em 0 0.25em 0; | |||
| } | |||
| .mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { | |||
| background-color: #FBFBFB; | |||
| margin: 0; | |||
| padding: 0.25ex; | |||
| padding: 0; | |||
| } | |||
| .mdescLeft,.mdescRight { | |||
| @@ -329,12 +308,18 @@ table.memberdecls { | |||
| } | |||
| .memItemLeft,.memItemRight,.memTemplParams { | |||
| border-top: 1px solid #DDD; | |||
| border: 0; | |||
| font-family: monospace, fixed; | |||
| } | |||
| .memItemLeft,.memTemplItemLeft { | |||
| white-space: nowrap; | |||
| padding-left: 2em; | |||
| padding-right: 1em; | |||
| } | |||
| .memItemLeft a.el { | |||
| font-weight: bold; | |||
| } | |||
| .memTemplParams { | |||
| @@ -342,11 +327,24 @@ table.memberdecls { | |||
| 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 { | |||
| font-size: 80%; | |||
| color: #4665A2; | |||
| font-weight: bold; | |||
| } | |||
| @@ -362,26 +360,22 @@ table.memberdecls { | |||
| .memitem { | |||
| padding: 0; | |||
| margin: 1ex 0 2ex 0; | |||
| border: 1px solid #CCC; | |||
| margin: 1em 0 1em 0; | |||
| } | |||
| .memname { | |||
| white-space: nowrap; | |||
| .memproto { | |||
| padding: 0; | |||
| font-weight: bold; | |||
| color: #000; | |||
| } | |||
| .memproto { | |||
| border-bottom: 1px solid #DDD; | |||
| padding: 0.5ex; | |||
| font-weight: bold; | |||
| background-color: #F3F3F3; | |||
| .memproto .paramname { | |||
| color: #444; | |||
| font-style: normal; | |||
| } | |||
| .memdoc { | |||
| padding: 1ex; | |||
| background-color: #FBFBFB; | |||
| border-top-width: 0; | |||
| padding: 0 0 0.5em 2em; | |||
| } | |||
| .paramkey { | |||
| @@ -389,16 +383,37 @@ table.memberdecls { | |||
| } | |||
| .paramtype { | |||
| color: #3E873E; | |||
| white-space: nowrap; | |||
| } | |||
| .paramname { | |||
| color: #602020; | |||
| color: #444; | |||
| white-space: nowrap; | |||
| font-weight: bold; | |||
| } | |||
| .paramname em { | |||
| font-style: normal; | |||
| 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 */ | |||
| @@ -411,9 +426,8 @@ table.memberdecls { | |||
| /* these are for tree view when used as main index */ | |||
| .directory { | |||
| font-size: 9pt; | |||
| font-weight: bold; | |||
| margin: 5px; | |||
| font-size: small; | |||
| margin: 0.5em; | |||
| } | |||
| .directory h3 { | |||
| @@ -482,7 +496,7 @@ address { | |||
| table.doxtable { | |||
| border-collapse: collapse; | |||
| margin: 0.5ex; | |||
| margin: 0.5em; | |||
| } | |||
| table.doxtable td,table.doxtable th { | |||
| @@ -508,12 +522,13 @@ table.doxtable th { | |||
| font-size: 13px; | |||
| } | |||
| div.navpath { | |||
| padding: 0.25em; | |||
| } | |||
| .navpath ul { | |||
| font-size: 11px; | |||
| height: 30px; | |||
| line-height: 30px; | |||
| font-size: x-small; | |||
| color: #8AA0CC; | |||
| border: 1px solid #C2CDE4; | |||
| overflow: hidden; | |||
| margin: 0; | |||
| padding: 0; | |||
| @@ -528,7 +543,6 @@ table.doxtable th { | |||
| } | |||
| .navpath a { | |||
| height: 32px; | |||
| display: block; | |||
| text-decoration: none; | |||
| outline: none; | |||
| @@ -540,8 +554,8 @@ table.doxtable th { | |||
| div.summary { | |||
| float: right; | |||
| font-size: 8pt; | |||
| padding-right: 5px; | |||
| font-size: x-small; | |||
| padding: 0.25em 0.5em 0 0; | |||
| width: 50%; | |||
| text-align: right; | |||
| } | |||
| @@ -553,11 +567,125 @@ div.summary a { | |||
| div.header { | |||
| background-color: #F3F3F3; | |||
| margin: 0; | |||
| border-bottom: 1px solid #DDD; | |||
| border: 0; | |||
| } | |||
| div.headertitle { | |||
| padding: 5px 5px 5px 10px; | |||
| 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; | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2014 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -67,8 +67,10 @@ extern "C" { | |||
| #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" | |||
| @@ -578,8 +580,8 @@ lilv_world_load_all(LilvWorld* world, const char* lv2_path); | |||
| other things) MUST be identified by URIs (not paths) in save files. | |||
| */ | |||
| LILV_API void | |||
| lilv_world_load_bundle(LilvWorld* world, | |||
| LilvNode* bundle_uri); | |||
| lilv_world_load_bundle(LilvWorld* world, | |||
| const LilvNode* bundle_uri); | |||
| /** | |||
| Load all specifications from currently loaded bundles. | |||
| @@ -606,11 +608,11 @@ lilv_world_load_plugin_classes(LilvWorld* world); | |||
| 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 liv_world_load_resource(), they must be | |||
| 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, LilvNode* bundle_uri); | |||
| lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri); | |||
| /** | |||
| Load all the data associated with the given `resource`. | |||
| @@ -706,6 +708,16 @@ lilv_world_ask(LilvWorld* world, | |||
| 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 | |||
| @@ -751,7 +763,7 @@ lilv_plugin_get_uri(const LilvPlugin* plugin); | |||
| 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_uri_to_path(). | |||
| filesystem path, use lilv_file_uri_parse(). | |||
| @return a shared string which must not be modified or freed. | |||
| */ | |||
| LILV_API const LilvNode* | |||
| @@ -761,7 +773,7 @@ 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_uri_to_path(). | |||
| 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. | |||
| */ | |||
| @@ -771,7 +783,7 @@ 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_uri_to_path(). | |||
| filesystem path, use lilv_file_uri_parse(). | |||
| @return a shared string which must not be modified or freed. | |||
| */ | |||
| LILV_API const LilvNode* | |||
| @@ -1381,6 +1393,28 @@ 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. | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2015 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -26,14 +26,15 @@ lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
| 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); | |||
| char* const bundle_path = lilv_file_uri_parse( | |||
| lilv_node_as_uri(lilv_plugin_get_bundle_uri(plugin)), 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); | |||
| 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) { | |||
| @@ -41,15 +42,6 @@ lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
| return NULL; | |||
| } | |||
| // Parse bundle URI to use as base URI | |||
| const char* bundle_uri_str = lilv_node_as_uri(bundle_uri); | |||
| SerdURI base_uri; | |||
| if (serd_uri_parse((const uint8_t*)bundle_uri_str, &base_uri)) { | |||
| lilv_lib_close(lib); | |||
| lilv_free(bundle_path); | |||
| return NULL; | |||
| } | |||
| const LV2_Feature** local_features = NULL; | |||
| if (features == NULL) { | |||
| local_features = (const LV2_Feature**)malloc(sizeof(LV2_Feature*)); | |||
| @@ -67,18 +59,7 @@ lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
| break; // return NULL | |||
| } | |||
| // Resolve library plugin URI against base URI | |||
| SerdURI abs_uri; | |||
| SerdNode abs_uri_node = serd_node_new_uri_from_string( | |||
| (const uint8_t*)ld->URI, &base_uri, &abs_uri); | |||
| if (!abs_uri_node.buf) { | |||
| LILV_ERRORF("Failed to parse plugin URI `%s'\n", ld->URI); | |||
| lilv_lib_close(lib); | |||
| break; | |||
| } | |||
| if (!strcmp((const char*)abs_uri_node.buf, | |||
| lilv_node_as_uri(lilv_plugin_get_uri(plugin)))) { | |||
| 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; | |||
| @@ -86,10 +67,7 @@ lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
| ld, sample_rate, bundle_path, | |||
| (features) ? features : local_features); | |||
| result->pimpl = lib; | |||
| serd_node_free(&abs_uri_node); | |||
| break; | |||
| } else { | |||
| serd_node_free(&abs_uri_node); | |||
| } | |||
| } | |||
| @@ -97,15 +75,17 @@ lilv_plugin_instantiate(const LilvPlugin* plugin, | |||
| lilv_free(bundle_path); | |||
| if (result) { | |||
| // Failed to instantiate | |||
| 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) | |||
| 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; | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -56,7 +56,8 @@ lilv_lib_open(LilvWorld* world, | |||
| if (ldf) { | |||
| desc = ldf(bundle_path, features); | |||
| if (!desc) { | |||
| LILV_ERRORF("Call to `lv2_lib_descriptor' in %s failed\n", lib_path); | |||
| LILV_ERRORF("Call to %s:lv2_lib_descriptor failed\n", lib_path); | |||
| dlclose(lib); | |||
| lilv_free(lib_path); | |||
| return NULL; | |||
| } | |||
| @@ -87,8 +88,7 @@ lilv_lib_get_plugin(LilvLib* lib, uint32_t index) | |||
| { | |||
| if (lib->lv2_descriptor) { | |||
| return lib->lv2_descriptor(index); | |||
| } | |||
| if (lib->desc) { | |||
| } else if (lib->desc) { | |||
| return lib->desc->get_plugin(lib->desc->handle, index); | |||
| } | |||
| return NULL; | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2014 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -43,7 +43,7 @@ extern "C" { | |||
| #ifndef NAN | |||
| # define NAN INFINITY - INFINITY | |||
| #endif | |||
| static inline char* dlerror(void) { return "Unknown error"; } | |||
| static inline const char* dlerror(void) { return "Unknown error"; } | |||
| #else | |||
| # include <dlfcn.h> | |||
| # include <unistd.h> | |||
| @@ -127,6 +127,7 @@ struct LilvPluginImpl { | |||
| LilvPort** ports; | |||
| uint32_t num_ports; | |||
| bool loaded; | |||
| bool parse_errors; | |||
| bool replaced; | |||
| }; | |||
| @@ -156,6 +157,7 @@ struct LilvWorldImpl { | |||
| LilvPluginClasses* plugin_classes; | |||
| LilvSpec* specs; | |||
| LilvPlugins* plugins; | |||
| LilvPlugins* zombies; | |||
| LilvNodes* loaded_files; | |||
| ZixTree* libs; | |||
| struct { | |||
| @@ -172,7 +174,9 @@ struct LilvWorldImpl { | |||
| 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; | |||
| @@ -181,6 +185,8 @@ struct LilvWorldImpl { | |||
| SordNode* lv2_requiredFeature; | |||
| SordNode* lv2_symbol; | |||
| SordNode* lv2_prototype; | |||
| SordNode* mod_builderVersion; | |||
| SordNode* mod_releaseNumber; | |||
| SordNode* owl_Ontology; | |||
| SordNode* pset_value; | |||
| SordNode* rdf_a; | |||
| @@ -233,6 +239,13 @@ struct LilvUIImpl { | |||
| LilvNodes* classes; | |||
| }; | |||
| typedef struct LilvVersion { | |||
| int builder; | |||
| int minor; | |||
| int micro; | |||
| int release; | |||
| } LilvVersion; | |||
| /* | |||
| * | |||
| * Functions | |||
| @@ -248,6 +261,7 @@ 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* p); | |||
| void lilv_plugin_free(LilvPlugin* plugin); | |||
| LilvNode* lilv_plugin_get_unique(const LilvPlugin* p, | |||
| @@ -282,7 +296,8 @@ 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, LilvNode* bundle_uri); | |||
| LilvNode* lilv_world_get_manifest_uri(LilvWorld* world, | |||
| const LilvNode* bundle_uri); | |||
| const uint8_t* lilv_world_blank_node_prefix(LilvWorld* world); | |||
| @@ -311,6 +326,55 @@ 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; | |||
| } | |||
| } | |||
| static inline int | |||
| lilv_version_cmp_mod(const LilvVersion* a, const LilvVersion* b) | |||
| { | |||
| // same version | |||
| if (a->builder == b->builder && a->minor == b->minor | |||
| && a->micro == b->micro && a->release == b->release) | |||
| return 0; | |||
| // check builder | |||
| if (a->builder < b->builder) | |||
| return -1; | |||
| if (a->builder > b->builder) | |||
| return 1; | |||
| // check minor | |||
| if (a->minor < b->minor) | |||
| return -1; | |||
| if (a->minor > b->minor) | |||
| return 1; | |||
| // check micro | |||
| if (a->micro < b->micro) | |||
| return -1; | |||
| if (a->micro > b->micro) | |||
| return 1; | |||
| // check release | |||
| if (a->release < b->release) | |||
| return -1; | |||
| if (a->release > b->release) | |||
| return 1; | |||
| // huh!? | |||
| return 0; | |||
| } | |||
| struct LilvHeader* | |||
| lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri); | |||
| @@ -405,6 +469,10 @@ static const LV2_Feature* const dman_features = { NULL }; | |||
| __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 | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2015 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -15,16 +15,16 @@ | |||
| */ | |||
| #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, size_t len) | |||
| lilv_node_set_numerics_from_string(LilvNode* val) | |||
| { | |||
| const char* str = (const char*)sord_node_get_string(val->node); | |||
| char* endptr; | |||
| switch (val->type) { | |||
| case LILV_VALUE_URI: | |||
| @@ -33,10 +33,10 @@ lilv_node_set_numerics_from_string(LilvNode* val, size_t len) | |||
| case LILV_VALUE_BLOB: | |||
| break; | |||
| case LILV_VALUE_INT: | |||
| val->val.int_val = strtol(str, &endptr, 10); | |||
| val->val.int_val = strtol(str, NULL, 10); | |||
| break; | |||
| case LILV_VALUE_FLOAT: | |||
| val->val.float_val = serd_strtod(str, &endptr); | |||
| val->val.float_val = serd_strtod(str, NULL); | |||
| break; | |||
| case LILV_VALUE_BOOL: | |||
| val->val.bool_val = !strcmp(str, "true"); | |||
| @@ -104,7 +104,6 @@ lilv_node_new_from_node(LilvWorld* world, const SordNode* node) | |||
| LilvNode* result = NULL; | |||
| SordNode* datatype_uri = NULL; | |||
| LilvNodeType type = LILV_VALUE_STRING; | |||
| size_t len = 0; | |||
| switch (sord_node_get_type(node)) { | |||
| case SORD_URI: | |||
| @@ -137,8 +136,8 @@ lilv_node_new_from_node(LilvWorld* world, const SordNode* node) | |||
| sord_node_get_string(datatype_uri)); | |||
| } | |||
| result = lilv_node_new( | |||
| world, type, (const char*)sord_node_get_string_counted(node, &len)); | |||
| lilv_node_set_numerics_from_string(result, len); | |||
| world, type, (const char*)sord_node_get_string(node)); | |||
| lilv_node_set_numerics_from_string(result); | |||
| break; | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2014 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -33,14 +33,9 @@ | |||
| #define NS_FOAF (const uint8_t*)"http://xmlns.com/foaf/0.1/" | |||
| #define NS_MOD (const uint8_t*)"http://moddevices.com/ns/modgui#" | |||
| /** Ownership of `uri` is taken */ | |||
| LilvPlugin* | |||
| lilv_plugin_new(LilvWorld* world, LilvNode* uri, LilvNode* bundle_uri) | |||
| static void | |||
| lilv_plugin_init(LilvPlugin* plugin, LilvNode* bundle_uri) | |||
| { | |||
| assert(bundle_uri); | |||
| LilvPlugin* plugin = (LilvPlugin*)malloc(sizeof(LilvPlugin)); | |||
| plugin->world = world; | |||
| plugin->plugin_uri = uri; | |||
| plugin->bundle_uri = bundle_uri; | |||
| plugin->binary_uri = NULL; | |||
| #ifdef LILV_DYN_MANIFEST | |||
| @@ -51,11 +46,32 @@ lilv_plugin_new(LilvWorld* world, LilvNode* uri, LilvNode* bundle_uri) | |||
| 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* p) | |||
| { | |||
| @@ -113,7 +129,8 @@ lilv_plugin_get_one(const LilvPlugin* p, | |||
| SordIter* stream = lilv_world_query_internal( | |||
| p->world, subject, predicate, NULL); | |||
| if (!sord_iter_end(stream)) { | |||
| ret = lilv_node_new_from_node(p->world, sord_iter_get_node(stream, SORD_OBJECT)); | |||
| ret = lilv_node_new_from_node(p->world, | |||
| sord_iter_get_node(stream, SORD_OBJECT)); | |||
| } | |||
| sord_iter_free(stream); | |||
| return ret; | |||
| @@ -126,7 +143,7 @@ lilv_plugin_get_unique(const LilvPlugin* p, | |||
| { | |||
| LilvNode* ret = lilv_plugin_get_one(p, subject, predicate); | |||
| if (!ret) { | |||
| LILV_ERRORF("Multiple values found for (%s %s ...) property\n", | |||
| LILV_ERRORF("No value found for (%s %s ...) property\n", | |||
| sord_node_get_string(subject), | |||
| sord_node_get_string(predicate)); | |||
| } | |||
| @@ -143,13 +160,13 @@ lilv_plugin_load(LilvPlugin* p) | |||
| SerdReader* reader = sord_new_reader(p->world->model, env, SERD_TURTLE, | |||
| bundle_uri_node); | |||
| SordModel* prototypes = lilv_world_filter_model(p->world, | |||
| p->world->model, | |||
| p->plugin_uri->node, | |||
| p->world->uris.lv2_prototype, | |||
| NULL, NULL); | |||
| SordModel* prots = lilv_world_filter_model(p->world, | |||
| p->world->model, | |||
| p->plugin_uri->node, | |||
| p->world->uris.lv2_prototype, | |||
| NULL, NULL); | |||
| SordModel* skel = sord_new(p->world->world, SORD_SPO, false); | |||
| SordIter* iter = sord_begin(prototypes); | |||
| 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(p->world, t); | |||
| @@ -177,14 +194,26 @@ lilv_plugin_load(LilvPlugin* p) | |||
| } | |||
| sord_iter_free(iter); | |||
| sord_free(skel); | |||
| sord_free(prototypes); | |||
| sord_free(prots); | |||
| // Parse all the plugin's data files into RDF model | |||
| SerdStatus st = SERD_SUCCESS; | |||
| LILV_FOREACH(nodes, i, p->data_uris) { | |||
| const LilvNode* data_uri = lilv_nodes_get(p->data_uris, i); | |||
| serd_env_set_base_uri(env, sord_node_to_serd_node(data_uri->node)); | |||
| lilv_world_load_file(p->world, reader, data_uri); | |||
| st = lilv_world_load_file(p->world, reader, data_uri); | |||
| if (st > SERD_FAILURE) { | |||
| break; | |||
| } | |||
| } | |||
| if (st > SERD_FAILURE) { | |||
| p->loaded = true; | |||
| p->parse_errors = true; | |||
| serd_reader_free(reader); | |||
| serd_env_free(env); | |||
| return; | |||
| } | |||
| #ifdef LILV_DYN_MANIFEST | |||
| @@ -235,8 +264,8 @@ static void | |||
| lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p) | |||
| { | |||
| LilvPlugin* p = (LilvPlugin*)const_p; | |||
| if (!p->loaded) | |||
| lilv_plugin_load(p); | |||
| lilv_plugin_load_if_necessary(p); | |||
| if (!p->ports) { | |||
| p->ports = (LilvPort**)malloc(sizeof(LilvPort*)); | |||
| @@ -255,21 +284,24 @@ lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p) | |||
| LilvNode* symbol = lilv_plugin_get_unique( | |||
| p, port, p->world->uris.lv2_symbol); | |||
| bool error = false; | |||
| 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(p->plugin_uri), | |||
| lilv_node_as_string(symbol)); | |||
| error = true; | |||
| goto done; | |||
| lilv_node_free(symbol); | |||
| lilv_node_free(index); | |||
| lilv_plugin_free_ports(p); | |||
| break; | |||
| } | |||
| if (!lilv_node_is_int(index)) { | |||
| LILV_ERRORF("Plugin <%s> port index is not an integer\n", | |||
| lilv_node_as_uri(p->plugin_uri)); | |||
| error = true; | |||
| goto done; | |||
| lilv_node_free(symbol); | |||
| lilv_node_free(index); | |||
| lilv_plugin_free_ports(p); | |||
| break; | |||
| } | |||
| uint32_t this_index = lilv_node_as_int(index); | |||
| @@ -308,13 +340,8 @@ lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p) | |||
| } | |||
| sord_iter_free(types); | |||
| done: | |||
| lilv_node_free(symbol); | |||
| lilv_node_free(index); | |||
| if (error) { // Invalid plugin | |||
| lilv_plugin_free_ports(p); | |||
| break; | |||
| } | |||
| } | |||
| sord_iter_free(ports); | |||
| @@ -389,12 +416,12 @@ lilv_plugin_get_class(const LilvPlugin* const_p) | |||
| lilv_plugin_load_if_necessary(p); | |||
| if (!p->plugin_class) { | |||
| // <plugin> a ?class | |||
| SordIter* results = lilv_world_query_internal(p->world, | |||
| p->plugin_uri->node, | |||
| p->world->uris.rdf_a, | |||
| NULL); | |||
| FOREACH_MATCH(results) { | |||
| const SordNode* class_node = sord_iter_get_node(results, SORD_OBJECT); | |||
| SordIter* c = lilv_world_query_internal(p->world, | |||
| p->plugin_uri->node, | |||
| p->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; | |||
| } | |||
| @@ -413,7 +440,7 @@ lilv_plugin_get_class(const LilvPlugin* const_p) | |||
| lilv_node_free(klass); | |||
| } | |||
| sord_iter_free(results); | |||
| sord_iter_free(c); | |||
| if (p->plugin_class == NULL) | |||
| p->plugin_class = p->world->lv2_plugin_class; | |||
| @@ -433,6 +460,11 @@ lilv_plugin_get_value_internal(const LilvPlugin* p, | |||
| 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); | |||
| @@ -867,46 +899,36 @@ lilv_plugin_get_author(const LilvPlugin* p) | |||
| return author; | |||
| } | |||
| LILV_API LilvNode* | |||
| lilv_plugin_get_author_name(const LilvPlugin* plugin) | |||
| 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* foaf_name = sord_new_uri(sworld, NS_FOAF "name"); | |||
| LilvNode* ret = lilv_plugin_get_one(plugin, author, foaf_name); | |||
| sord_node_free(sworld, foaf_name); | |||
| 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) | |||
| { | |||
| const SordNode* author = lilv_plugin_get_author(plugin); | |||
| if (author) { | |||
| SordWorld* sworld = plugin->world->world; | |||
| SordNode* foaf_mbox = sord_new_uri(sworld, NS_FOAF "mbox"); | |||
| LilvNode* ret = lilv_plugin_get_one(plugin, author, foaf_mbox); | |||
| sord_node_free(sworld, foaf_mbox); | |||
| return ret; | |||
| } | |||
| return NULL; | |||
| return lilv_plugin_get_author_property(plugin, NS_FOAF "mbox"); | |||
| } | |||
| LILV_API LilvNode* | |||
| lilv_plugin_get_author_homepage(const LilvPlugin* plugin) | |||
| { | |||
| const SordNode* author = lilv_plugin_get_author(plugin); | |||
| if (author) { | |||
| SordWorld* sworld = plugin->world->world; | |||
| SordNode* foaf_homepage = sord_new_uri(sworld, NS_FOAF "homepage"); | |||
| LilvNode* ret = lilv_plugin_get_one(plugin, author, foaf_homepage); | |||
| sord_node_free(sworld, foaf_homepage); | |||
| return ret; | |||
| } | |||
| return NULL; | |||
| return lilv_plugin_get_author_property(plugin, NS_FOAF "homepage"); | |||
| } | |||
| static const SordNode* | |||
| @@ -1060,7 +1082,10 @@ lilv_plugin_get_uis(const LilvPlugin* p) | |||
| const SordNode* ui = sord_iter_get_node(uis, SORD_OBJECT); | |||
| LilvNode* type = lilv_plugin_get_unique(p, ui, p->world->uris.rdf_a); | |||
| LilvNode* binary = lilv_plugin_get_unique(p, ui, ui_binary_node); | |||
| LilvNode* binary = lilv_plugin_get_one(p, ui, p->world->uris.lv2_binary); | |||
| if (!binary) { | |||
| binary = lilv_plugin_get_unique(p, ui, ui_binary_node); | |||
| } | |||
| if (sord_node_get_type(ui) != SORD_URI | |||
| || !lilv_node_is_uri(type) | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2015 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -49,21 +49,26 @@ typedef struct { | |||
| 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 | |||
| Property* props; ///< State properties | |||
| PortValue* values; ///< Port values | |||
| uint32_t atom_Path; ///< atom:Path URID | |||
| uint32_t num_props; ///< Number of state properties | |||
| uint32_t num_values; ///< Number of port values | |||
| 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 | |||
| @@ -106,18 +111,18 @@ append_port_value(LilvState* state, | |||
| uint32_t size, | |||
| uint32_t type) | |||
| { | |||
| PortValue* pv = NULL; | |||
| if (value) { | |||
| state->values = (PortValue*)realloc( | |||
| state->values, (++state->num_values) * sizeof(PortValue)); | |||
| PortValue* pv = &state->values[state->num_values - 1]; | |||
| 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; | |||
| } | |||
| return NULL; | |||
| return pv; | |||
| } | |||
| static const char* | |||
| @@ -131,24 +136,23 @@ lilv_state_rel2abs(const LilvState* state, const char* path) | |||
| return path; | |||
| } | |||
| 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) | |||
| static void | |||
| append_property(LilvState* state, | |||
| PropertyArray* array, | |||
| uint32_t key, | |||
| const void* value, | |||
| size_t size, | |||
| uint32_t type, | |||
| uint32_t flags) | |||
| { | |||
| LilvState* const state = (LilvState*)handle; | |||
| state->props = (Property*)realloc( | |||
| state->props, (++state->num_props) * sizeof(Property)); | |||
| Property* const prop = &state->props[state->num_props - 1]; | |||
| 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 { | |||
| LILV_WARN("Storing non-POD value\n"); | |||
| prop->value = (void*)value; | |||
| } | |||
| @@ -156,7 +160,18 @@ store_callback(LV2_State_Handle handle, | |||
| 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; | |||
| } | |||
| @@ -170,7 +185,7 @@ retrieve_callback(LV2_State_Handle handle, | |||
| 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, state->num_props, | |||
| &search_key, state->props.props, state->props.n, | |||
| sizeof(Property), property_cmp); | |||
| if (prop) { | |||
| @@ -230,6 +245,7 @@ abstract_path(LV2_State_Map_Path_Handle handle, | |||
| 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); | |||
| const int st = lilv_copy_file(real_path, copy); | |||
| if (st) { | |||
| @@ -388,15 +404,15 @@ lilv_state_new_from_instance(const LilvPlugin* plugin, | |||
| instance->lv2_handle, store_callback, state, flags, features); | |||
| if (st) { | |||
| LILV_ERRORF("Error saving plugin state: %s\n", state_strerror(st)); | |||
| free(state->props); | |||
| state->props = NULL; | |||
| state->num_props = 0; | |||
| free(state->props.props); | |||
| state->props.props = NULL; | |||
| state->props.n = 0; | |||
| } else { | |||
| qsort(state->props, state->num_props, sizeof(Property), property_cmp); | |||
| qsort(state->props.props, state->props.n, sizeof(Property), property_cmp); | |||
| } | |||
| } | |||
| qsort(state->values, state->num_values, sizeof(PortValue), value_cmp); | |||
| qsort(state->values, state->n_values, sizeof(PortValue), value_cmp); | |||
| free(sfeatures); | |||
| return state; | |||
| @@ -407,7 +423,7 @@ lilv_state_emit_port_values(const LilvState* state, | |||
| LilvSetPortValueFunc set_value, | |||
| void* user_data) | |||
| { | |||
| for (uint32_t i = 0; i < state->num_values; ++i) { | |||
| 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); | |||
| } | |||
| @@ -422,7 +438,7 @@ lilv_state_restore(const LilvState* state, | |||
| uint32_t flags, | |||
| const LV2_Feature *const * features) | |||
| { | |||
| if (iface) { | |||
| if (iface && iface->restore) { | |||
| iface->restore(handle, retrieve_callback, | |||
| (LV2_State_Handle)state, flags, features); | |||
| } | |||
| @@ -477,7 +493,7 @@ new_state_from_model(LilvWorld* world, | |||
| 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) { | |||
| if (!state->dir && graph) { | |||
| state->dir = lilv_strdup((const char*)sord_node_get_string(graph)); | |||
| } | |||
| sord_iter_free(i); | |||
| @@ -531,8 +547,9 @@ new_state_from_model(LilvWorld* world, | |||
| 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 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( | |||
| @@ -543,19 +560,19 @@ new_state_from_model(LilvWorld* world, | |||
| uint32_t flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; | |||
| Property prop = { NULL, 0, 0, 0, flags }; | |||
| prop.key = map->map(map->handle, (const char*)sord_node_get_string(p)); | |||
| 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_PORTABLE; | |||
| prop.flags = LV2_STATE_IS_POD; | |||
| } | |||
| if (prop.value) { | |||
| state->props = (Property*)realloc( | |||
| state->props, (++state->num_props) * sizeof(Property)); | |||
| state->props[state->num_props - 1] = prop; | |||
| 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); | |||
| @@ -566,8 +583,12 @@ new_state_from_model(LilvWorld* world, | |||
| free((void*)chunk.buf); | |||
| sratom_free(sratom); | |||
| qsort(state->props, state->num_props, sizeof(Property), property_cmp); | |||
| qsort(state->values, state->num_values, sizeof(PortValue), value_cmp); | |||
| 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; | |||
| } | |||
| @@ -683,7 +704,9 @@ ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env) | |||
| SerdWriter* writer = serd_writer_new( | |||
| SERD_TURTLE, | |||
| (SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED), | |||
| (SerdStyle)(SERD_STYLE_RESOLVED | | |||
| SERD_STYLE_ABBREVIATED| | |||
| SERD_STYLE_CURIED), | |||
| env, | |||
| &base_uri, | |||
| sink, | |||
| @@ -839,6 +862,37 @@ link_exists(const char* path, void* data) | |||
| 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, | |||
| @@ -885,11 +939,14 @@ lilv_state_write(LilvWorld* world, | |||
| (SerdEndSink)serd_writer_end_anon, | |||
| writer); | |||
| // Write port values as pretty numbers | |||
| sratom_set_pretty_numbers(sratom, true); | |||
| // 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 | |||
| for (uint32_t i = 0; i < state->num_values; ++i) { | |||
| 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( | |||
| @@ -914,35 +971,19 @@ lilv_state_write(LilvWorld* world, | |||
| serd_writer_end_anon(writer, &port); | |||
| } | |||
| // Write property values with precise types | |||
| sratom_set_pretty_numbers(sratom, false); | |||
| // Write properties | |||
| const SerdNode state_node = serd_node_from_string(SERD_BLANK, | |||
| USTR("2state")); | |||
| if (state->num_props > 0) { | |||
| 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, &state_node, NULL, NULL); | |||
| &subject, &p, &body, NULL, NULL); | |||
| } | |||
| for (uint32_t i = 0; i < state->num_props; ++i) { | |||
| Property* prop = &state->props[i]; | |||
| const char* key = unmap->unmap(unmap->handle, prop->key); | |||
| sratom_set_pretty_numbers(sratom, false); // Use precise types | |||
| write_property_array(state, &state->props, sratom, SERD_ANON_CONT, | |||
| &body, unmap, dir); | |||
| 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); | |||
| sratom_write(sratom, unmap, SERD_ANON_CONT, | |||
| &state_node, &p, prop->type, | |||
| strlen(abs_path) + 1, abs_path); | |||
| } else { | |||
| sratom_write(sratom, unmap, SERD_ANON_CONT, | |||
| &state_node, &p, prop->type, prop->size, prop->value); | |||
| } | |||
| } | |||
| if (state->num_props > 0) { | |||
| serd_writer_end_anon(writer, &state_node); | |||
| if (state->props.n > 0) { | |||
| serd_writer_end_anon(writer, &body); | |||
| } | |||
| sratom_free(sratom); | |||
| @@ -1099,12 +1140,12 @@ lilv_state_delete(LilvWorld* world, | |||
| model, state->uri->node, world->uris.rdfs_seeAlso, NULL, NULL); | |||
| if (file) { | |||
| // Remove state file | |||
| char* file_path = lilv_file_uri_parse( | |||
| char* path = lilv_file_uri_parse( | |||
| (const char*)sord_node_get_string(file), NULL); | |||
| if (unlink(file_path)) { | |||
| LILV_ERRORF("Failed to remove %s (%s)\n", file_path, strerror(errno)); | |||
| if (unlink(path)) { | |||
| LILV_ERRORF("Failed to remove %s (%s)\n", path, strerror(errno)); | |||
| } | |||
| lilv_free(file_path); | |||
| lilv_free(path); | |||
| } | |||
| // Remove any existing manifest entries for this state | |||
| @@ -1141,14 +1182,26 @@ lilv_state_delete(LilvWorld* world, | |||
| 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) { | |||
| for (uint32_t i = 0; i < state->num_props; ++i) { | |||
| free(state->props[i].value); | |||
| } | |||
| for (uint32_t i = 0; i < state->num_values; ++i) { | |||
| 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); | |||
| } | |||
| @@ -1156,7 +1209,6 @@ lilv_state_free(LilvState* state) | |||
| lilv_node_free(state->uri); | |||
| zix_tree_free(state->abs2rel); | |||
| zix_tree_free(state->rel2abs); | |||
| free(state->props); | |||
| free(state->values); | |||
| free(state->label); | |||
| free(state->dir); | |||
| @@ -1174,12 +1226,12 @@ lilv_state_equals(const LilvState* a, const LilvState* b) | |||
| || (a->label && !b->label) | |||
| || (b->label && !a->label) | |||
| || (a->label && b->label && strcmp(a->label, b->label)) | |||
| || a->num_props != b->num_props | |||
| || a->num_values != b->num_values) { | |||
| || a->props.n != b->props.n | |||
| || a->n_values != b->n_values) { | |||
| return false; | |||
| } | |||
| for (uint32_t i = 0; i < a->num_values; ++i) { | |||
| 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 | |||
| @@ -1189,9 +1241,9 @@ lilv_state_equals(const LilvState* a, const LilvState* b) | |||
| } | |||
| } | |||
| for (uint32_t i = 0; i < a->num_props; ++i) { | |||
| Property* const ap = &a->props[i]; | |||
| Property* const bp = &b->props[i]; | |||
| 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) { | |||
| @@ -1213,7 +1265,7 @@ lilv_state_equals(const LilvState* a, const LilvState* b) | |||
| LILV_API unsigned | |||
| lilv_state_get_num_properties(const LilvState* state) | |||
| { | |||
| return state->num_props; | |||
| return state->props.n; | |||
| } | |||
| LILV_API const LilvNode* | |||
| @@ -1241,3 +1293,15 @@ lilv_state_set_label(LilvState* state, const char* 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; | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| --- src/state.c | |||
| +++ src/state.c | |||
| @@ -415,35 +415,18 @@ | |||
| LILV_API void | |||
| lilv_state_restore(const LilvState* state, | |||
| - LilvInstance* instance, | |||
| + const LV2_State_Interface* iface, | |||
| + LV2_Handle handle, | |||
| 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 }; | |||
| - | |||
| - const LV2_Feature** sfeatures = add_features(features, &map_feature, NULL); | |||
| - | |||
| - const LV2_Descriptor* desc = instance ? instance->lv2_descriptor : NULL; | |||
| - const LV2_State_Interface* iface = (desc && desc->extension_data) | |||
| - ? (const LV2_State_Interface*)desc->extension_data(LV2_STATE__interface) | |||
| - : NULL; | |||
| - | |||
| if (iface) { | |||
| - iface->restore(instance->lv2_handle, retrieve_callback, | |||
| - (LV2_State_Handle)state, flags, sfeatures); | |||
| + iface->restore(handle, retrieve_callback, | |||
| + (LV2_State_Handle)state, flags, features); | |||
| } | |||
| - free(sfeatures); | |||
| - | |||
| if (set_value) { | |||
| lilv_state_emit_port_values(state, set_value, user_data); | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2015 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -16,6 +16,7 @@ | |||
| #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 */ | |||
| @@ -27,24 +28,27 @@ | |||
| #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 < 1500)) | |||
| /** Implement 'CreateSymbolicLink()' for MSVC 8 or earlier */ | |||
| 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"); | |||
| PFUNC pfn = (PFUNC)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), | |||
| "CreateSymbolicLinkA"); | |||
| return pfn ? pfn(linkpath, targetpath, flags) : 0; | |||
| } | |||
| # endif /* _MSC_VER < 1500 */ | |||
| #else | |||
| # include <dirent.h> | |||
| # include <limits.h> | |||
| @@ -61,7 +65,7 @@ CreateSymbolicLink(LPCTSTR linkpath, LPCTSTR targetpath, DWORD flags) | |||
| #endif | |||
| #ifndef PAGE_SIZE | |||
| # define PAGE_SIZE 4096 | |||
| # define PAGE_SIZE 4096 | |||
| #endif | |||
| void | |||
| @@ -296,6 +300,7 @@ lilv_copy_file(const char* src, const char* dst) | |||
| FILE* out = fopen(dst, "w"); | |||
| if (!out) { | |||
| fclose(in); | |||
| return errno; | |||
| } | |||
| @@ -370,26 +375,8 @@ lilv_path_join(const char* a, const char* b) | |||
| return path; | |||
| } | |||
| static void | |||
| lilv_size_mtime(const char* path, off_t* size, time_t* time) | |||
| { | |||
| struct stat buf; | |||
| if (stat(path, &buf)) { | |||
| LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); | |||
| return; | |||
| } | |||
| if (size) { | |||
| *size = buf.st_size; | |||
| } | |||
| if (time) { | |||
| *time = buf.st_mtime; | |||
| } | |||
| } | |||
| typedef struct { | |||
| char* pattern; | |||
| off_t orig_size; | |||
| time_t time; | |||
| char* latest; | |||
| } Latest; | |||
| @@ -397,16 +384,18 @@ typedef struct { | |||
| static void | |||
| update_latest(const char* path, const char* name, void* data) | |||
| { | |||
| Latest* latest = (Latest*)data; | |||
| char* entry_path = lilv_path_join(path, name); | |||
| Latest* latest = (Latest*)data; | |||
| char* entry_path = lilv_path_join(path, name); | |||
| unsigned num; | |||
| if (sscanf(entry_path, latest->pattern, &num) == 1) { | |||
| off_t entry_size = 0; | |||
| time_t entry_time = 0; | |||
| lilv_size_mtime(entry_path, &entry_size, &entry_time); | |||
| if (entry_size == latest->orig_size && entry_time >= latest->time) { | |||
| free(latest->latest); | |||
| latest->latest = entry_path; | |||
| 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) { | |||
| @@ -419,8 +408,14 @@ char* | |||
| lilv_get_latest_copy(const char* path, const char* copy_path) | |||
| { | |||
| char* copy_dir = lilv_dirname(copy_path); | |||
| Latest latest = { lilv_strjoin(copy_path, "%u", NULL), 0, 0, NULL }; | |||
| lilv_size_mtime(path, &latest.orig_size, &latest.time); | |||
| 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); | |||
| @@ -609,19 +604,16 @@ lilv_file_equals(const char* a_path, const char* b_path) | |||
| FILE* b_file = NULL; | |||
| char* const a_real = lilv_realpath(a_path); | |||
| char* const b_real = lilv_realpath(b_path); | |||
| if (!a_real || !b_real) { | |||
| match = false; // Missing file matches nothing | |||
| } else if (!strcmp(a_real, b_real)) { | |||
| if (!strcmp(a_real, b_real)) { | |||
| match = true; // Real paths match | |||
| } else if (lilv_file_size(a_path) != lilv_file_size(b_path)) { | |||
| match = false; // Sizes differ | |||
| } else if (!(a_file = fopen(a_real, "rb"))) { | |||
| match = false; // Missing file matches nothing | |||
| } else if (!(b_file = fopen(b_real, "rb"))) { | |||
| } else if (!(a_file = fopen(a_real, "rb")) || | |||
| !(b_file = fopen(b_real, "rb"))) { | |||
| match = false; // Missing file matches nothing | |||
| } else { | |||
| match = true; | |||
| // TODO: Improve performance by reading chunks | |||
| match = true; | |||
| while (!feof(a_file) && !feof(b_file)) { | |||
| if (fgetc(a_file) != fgetc(b_file)) { | |||
| match = false; | |||
| @@ -0,0 +1,637 @@ | |||
| /* | |||
| 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) | |||
| /** Implement 'CreateSymbolicLink()' for MSVC 8 or earlier */ | |||
| 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; | |||
| } | |||
| #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) | |||
| { | |||
| return !access(path, F_OK); | |||
| } | |||
| char* | |||
| lilv_find_free_path(const char* in_path, | |||
| bool (*exists)(const char*, void*), void* user_data) | |||
| { | |||
| const size_t in_path_len = strlen(in_path); | |||
| char* path = (char*)malloc(in_path_len + 7); | |||
| memcpy(path, in_path, in_path_len + 1); | |||
| for (int i = 2; i < 1000000; ++i) { | |||
| if (!exists(path, user_data)) { | |||
| return path; | |||
| } | |||
| snprintf(path, in_path_len + 7, "%s.%u", in_path, i); | |||
| } | |||
| return NULL; | |||
| } | |||
| int | |||
| lilv_copy_file(const char* src, const char* dst) | |||
| { | |||
| FILE* in = fopen(src, "r"); | |||
| if (!in) { | |||
| 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 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) { | |||
| long name_max = pathconf(path, _PC_NAME_MAX); | |||
| if (name_max == -1) { | |||
| name_max = 255; // Limit not defined, or error | |||
| } | |||
| const size_t len = offsetof(struct dirent, d_name) + name_max + 1; | |||
| struct dirent* entry = (struct dirent*)malloc(len); | |||
| struct dirent* result; | |||
| while (!readdir_r(dir, entry, &result) && result) { | |||
| f(path, entry->d_name, data); | |||
| } | |||
| free(entry); | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| --- src/util.c | |||
| +++ src/util.c | |||
| @@ -29,7 +29,6 @@ | |||
| #include <string.h> | |||
| #ifdef _WIN32 | |||
| -# define _WIN32_WINNT 0x0600 /* for CreateSymbolicLink */ | |||
| # include <windows.h> | |||
| # include <direct.h> | |||
| # include <io.h> | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2014 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -23,6 +23,9 @@ | |||
| #include "lilv_internal.h" | |||
| static int | |||
| lilv_world_drop_graph(LilvWorld* world, const SordNode* graph); | |||
| LILV_API LilvWorld* | |||
| lilv_world_new(void) | |||
| { | |||
| @@ -39,6 +42,7 @@ lilv_world_new(void) | |||
| 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); | |||
| @@ -63,7 +67,9 @@ lilv_world_new(void) | |||
| 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); | |||
| @@ -133,6 +139,13 @@ lilv_world_free(LilvWorld* world) | |||
| 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; | |||
| @@ -180,6 +193,9 @@ lilv_world_find_nodes(LilvWorld* world, | |||
| 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)); | |||
| @@ -278,12 +294,8 @@ lilv_new_uri_relative_to_base(const uint8_t* uri_str, | |||
| const uint8_t* base_uri_str) | |||
| { | |||
| SerdURI base_uri; | |||
| if (serd_uri_parse(base_uri_str, &base_uri)) { | |||
| return SERD_NODE_NULL; | |||
| } | |||
| SerdURI ignored; | |||
| return serd_node_new_uri_from_string(uri_str, &base_uri, &ignored); | |||
| serd_uri_parse(base_uri_str, &base_uri); | |||
| return serd_node_new_uri_from_string(uri_str, &base_uri, NULL); | |||
| } | |||
| const uint8_t* | |||
| @@ -322,23 +334,24 @@ lilv_lib_compare(const void* a, const void* b, void* user_data) | |||
| } | |||
| /** Get an element of a collection of any object with an LilvHeader by URI. */ | |||
| struct LilvHeader* | |||
| lilv_collection_get_by_uri(const ZixTree* const_seq, | |||
| const LilvNode* uri) | |||
| static ZixTreeIter* | |||
| lilv_collection_find_by_uri(const ZixTree* seq, const LilvNode* uri) | |||
| { | |||
| if (!lilv_node_is_uri(uri)) { | |||
| return NULL; | |||
| ZixTreeIter* i = NULL; | |||
| if (lilv_node_is_uri(uri)) { | |||
| struct LilvHeader key = { NULL, (LilvNode*)uri }; | |||
| zix_tree_find(seq, &key, &i); | |||
| } | |||
| return i; | |||
| } | |||
| ZixTree* seq = (ZixTree*)const_seq; | |||
| struct LilvHeader key = { NULL, (LilvNode*)uri }; | |||
| ZixTreeIter* i = NULL; | |||
| ZixStatus st = zix_tree_find(seq, &key, &i); | |||
| if (!st) { | |||
| return (struct LilvHeader*)zix_tree_get(i); | |||
| } | |||
| /** 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 NULL; | |||
| return i ? (struct LilvHeader*)zix_tree_get(i) : NULL; | |||
| } | |||
| static void | |||
| @@ -371,33 +384,55 @@ lilv_world_add_spec(LilvWorld* world, | |||
| } | |||
| static void | |||
| lilv_world_add_plugin(LilvWorld* world, | |||
| const SordNode* plugin_node, | |||
| const LilvNode* manifest_uri, | |||
| void* dynmanifest, | |||
| const SordNode* bundle_node) | |||
| { | |||
| LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node); | |||
| const LilvPlugin* last = lilv_plugins_get_by_uri(world->plugins, | |||
| plugin_uri); | |||
| if (last) { | |||
| LILV_ERRORF("Duplicate plugin <%s>\n", lilv_node_as_uri(plugin_uri)); | |||
| LILV_ERRORF("... found in %s\n", lilv_node_as_string( | |||
| lilv_plugin_get_bundle_uri(last))); | |||
| LILV_ERRORF("... and %s\n", sord_node_get_string(bundle_node)); | |||
| lilv_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); | |||
| return; | |||
| } | |||
| 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)); | |||
| // Create LilvPlugin | |||
| LilvNode* bundle_uri = lilv_node_new_from_node(world, bundle_node); | |||
| LilvPlugin* plugin = lilv_plugin_new(world, plugin_uri, bundle_uri); | |||
| // Add manifest as plugin data file (as if it were rdfs:seeAlso) | |||
| zix_tree_insert((ZixTree*)plugin->data_uris, | |||
| lilv_node_duplicate(manifest_uri), | |||
| NULL); | |||
| // Add plugin to world plugin sequence | |||
| zix_tree_insert((ZixTree*)world->plugins, plugin, NULL); | |||
| } | |||
| // 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); | |||
| #ifdef LILV_DYN_MANIFEST | |||
| // Set dynamic manifest library URI, if applicable | |||
| @@ -420,9 +455,6 @@ lilv_world_add_plugin(LilvWorld* world, | |||
| NULL); | |||
| } | |||
| sord_iter_free(files); | |||
| // Add plugin to world plugin sequence | |||
| zix_tree_insert((ZixTree*)world->plugins, plugin, NULL); | |||
| } | |||
| SerdStatus | |||
| @@ -480,7 +512,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| // Get binary path | |||
| const SordNode* binary = sord_iter_get_node(binaries, SORD_OBJECT); | |||
| const uint8_t* lib_uri = sord_node_get_string(binary); | |||
| const char* lib_path = lilv_uri_to_path((const char*)lib_uri); | |||
| 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); | |||
| @@ -494,6 +526,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| LILV_ERRORF("Failed to open dynmanifest library `%s' (%s)\n", | |||
| lib_path, dlerror()); | |||
| sord_iter_free(binaries); | |||
| lilv_free(lib_path); | |||
| continue; | |||
| } | |||
| @@ -505,6 +538,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| LILV_ERRORF("No `lv2_dyn_manifest_open' in `%s'\n", lib_path); | |||
| sord_iter_free(binaries); | |||
| dlclose(lib); | |||
| lilv_free(lib_path); | |||
| continue; | |||
| } | |||
| @@ -517,6 +551,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| lib_path); | |||
| sord_iter_free(binaries); | |||
| dlclose(lib); | |||
| lilv_free(lib_path); | |||
| continue; | |||
| } | |||
| @@ -563,6 +598,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| } | |||
| sord_iter_free(p); | |||
| sord_free(plugins); | |||
| lilv_free(lib_path); | |||
| } | |||
| sord_iter_free(iter); | |||
| sord_free(model); | |||
| @@ -570,7 +606,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, | |||
| } | |||
| LilvNode* | |||
| lilv_world_get_manifest_uri(LilvWorld* world, LilvNode* bundle_uri) | |||
| 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", | |||
| @@ -580,8 +616,67 @@ lilv_world_get_manifest_uri(LilvWorld* world, LilvNode* bundle_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, LilvNode* bundle_uri) | |||
| 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", | |||
| @@ -606,6 +701,88 @@ lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) | |||
| 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); | |||
| @@ -633,14 +810,14 @@ lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) | |||
| } | |||
| static int | |||
| lilv_world_drop_graph(LilvWorld* world, LilvNode* graph) | |||
| lilv_world_drop_graph(LilvWorld* world, const SordNode* graph) | |||
| { | |||
| SordIter* i = sord_search(world->model, NULL, NULL, NULL, graph->node); | |||
| 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", | |||
| lilv_node_as_uri(graph), serd_strerror(st)); | |||
| sord_node_get_string(graph), serd_strerror(st)); | |||
| return st; | |||
| } | |||
| } | |||
| @@ -651,7 +828,7 @@ lilv_world_drop_graph(LilvWorld* world, LilvNode* graph) | |||
| /** Remove loaded_files entry so file will be reloaded if requested. */ | |||
| static int | |||
| lilv_world_unload_file(LilvWorld* world, LilvNode* file) | |||
| lilv_world_unload_file(LilvWorld* world, const LilvNode* file) | |||
| { | |||
| ZixTreeIter* iter; | |||
| if (!zix_tree_find((ZixTree*)world->loaded_files, file, &iter)) { | |||
| @@ -662,19 +839,54 @@ lilv_world_unload_file(LilvWorld* world, LilvNode* file) | |||
| } | |||
| LILV_API int | |||
| lilv_world_unload_bundle(LilvWorld* world, LilvNode* bundle_uri) | |||
| lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri) | |||
| { | |||
| if (!bundle_uri) { | |||
| return 0; | |||
| } | |||
| // Remove loaded_files entry for manifest.ttl | |||
| LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle_uri); | |||
| lilv_world_unload_file(world, manifest); | |||
| lilv_node_free(manifest); | |||
| // 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); | |||
| return lilv_world_drop_graph(world, bundle_uri->node); | |||
| } | |||
| static void | |||
| @@ -903,7 +1115,7 @@ lilv_world_unload_resource(LilvWorld* world, | |||
| 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)) { | |||
| } else if (!lilv_world_drop_graph(world, file_node->node)) { | |||
| lilv_world_unload_file(world, file_node); | |||
| ++n_dropped; | |||
| } | |||
| @@ -932,3 +1144,51 @@ 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; | |||
| } | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2011 David Robillard <http://drobilla.net> | |||
| 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 | |||
| @@ -59,7 +59,7 @@ typedef enum { | |||
| ZIX_STATUS_NOT_FOUND, | |||
| ZIX_STATUS_EXISTS, | |||
| ZIX_STATUS_BAD_ARG, | |||
| ZIX_STATUS_BAD_PERMS, | |||
| ZIX_STATUS_BAD_PERMS | |||
| } ZixStatus; | |||
| /** | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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> . | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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" | |||
| ] . | |||
| @@ -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> . | |||
| @@ -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; | |||
| } | |||
| @@ -28,6 +28,7 @@ main(int argc, char** argv) | |||
| 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); | |||
| @@ -37,6 +38,7 @@ main(int argc, char** argv) | |||
| LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL); | |||
| TEST_ASSERT(!instance); | |||
| lilv_node_free(plugin_uri); | |||
| lilv_world_free(world); | |||
| return 0; | |||
| @@ -28,6 +28,7 @@ main(int argc, char** argv) | |||
| 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); | |||
| @@ -36,15 +37,21 @@ main(int argc, char** argv) | |||
| 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; | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Copyright 2007-2015 David Robillard <http://drobilla.net> | |||
| Copyright 2007-2016 David Robillard <http://drobilla.net> | |||
| Copyright 2008 Krzysztof Foltman | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| @@ -15,7 +15,8 @@ | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| #define _POSIX_C_SOURCE 200112L /* for setenv */ | |||
| #define _POSIX_C_SOURCE 200809L /* for setenv */ | |||
| #define _XOPEN_SOURCE 600 /* for mkstemp */ | |||
| #include <assert.h> | |||
| #include <ctype.h> | |||
| @@ -159,9 +160,9 @@ cleanup(void) | |||
| #define TEST_ASSERT(check) do {\ | |||
| test_count++;\ | |||
| if (!(check)) {\ | |||
| assert(false);\ | |||
| error_count++;\ | |||
| fprintf(stderr, "lilv_test.c:%d: error: %s\n", __LINE__, #check);\ | |||
| fprintf(stderr, "lilv_test.c:%d: error: test `%s' failed\n", __LINE__, #check);\ | |||
| assert(check);\ | |||
| }\ | |||
| } while (0) | |||
| @@ -252,6 +253,23 @@ test_value(void) | |||
| 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"); | |||
| @@ -345,6 +363,29 @@ test_util(void) | |||
| { | |||
| TEST_ASSERT(!lilv_realpath(NULL)); | |||
| char a_path[16]; | |||
| char b_path[16]; | |||
| strcpy(a_path, "copy_a_XXXXXX"); | |||
| strcpy(b_path, "copy_b_XXXXXX"); | |||
| 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; | |||
| } | |||
| @@ -1418,6 +1459,7 @@ test_ui(void) | |||
| 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, | |||
| @@ -1433,6 +1475,10 @@ get_port_value(const char* port_symbol, | |||
| *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); | |||
| @@ -1452,6 +1498,8 @@ set_port_value(const char* port_symbol, | |||
| 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); | |||
| @@ -1613,6 +1661,18 @@ test_state(void) | |||
| 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"); | |||
| @@ -1623,6 +1683,16 @@ test_state(void) | |||
| "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"; | |||
| @@ -1893,6 +1963,7 @@ test_string(void) | |||
| 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); | |||
| @@ -1940,6 +2011,209 @@ test_world(void) | |||
| /*****************************************************************************/ | |||
| 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 = (char*)malloc(strlen(LILV_TEST_DIR) + 32); | |||
| strcpy(old_bundle_path, LILV_TEST_DIR); | |||
| strcat(old_bundle_path, "old_version.lv2/"); | |||
| // 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 = (char*)malloc(strlen(LILV_TEST_DIR) + 32); | |||
| strcpy(new_bundle_path, LILV_TEST_DIR); | |||
| strcat(new_bundle_path, "new_version.lv2/"); | |||
| // 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), | |||
| @@ -1963,6 +2237,9 @@ static struct TestCase tests[] = { | |||
| TEST_CASE(string), | |||
| TEST_CASE(world), | |||
| TEST_CASE(state), | |||
| TEST_CASE(reload_bundle), | |||
| TEST_CASE(replace_version), | |||
| TEST_CASE(get_symbol), | |||
| { NULL, NULL } | |||
| }; | |||
| @@ -17,4 +17,4 @@ | |||
| #include "lv2/lv2plug.in/ns/lv2core/lv2.h" | |||
| LV2_SYMBOL_EXPORT const char* msg = "this is not the thing you're looking for"; | |||
| const char* msg = "this is not the thing you're looking for"; | |||
| @@ -28,6 +28,7 @@ main(int argc, char** argv) | |||
| 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); | |||
| @@ -37,6 +38,7 @@ main(int argc, char** argv) | |||
| LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL); | |||
| TEST_ASSERT(!instance); | |||
| lilv_node_free(plugin_uri); | |||
| lilv_world_free(world); | |||
| return 0; | |||
| @@ -1,5 +1,5 @@ | |||
| /* | |||
| Lilv Test Plugin - Missing descriptor | |||
| 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 | |||
| @@ -28,6 +28,7 @@ main(int argc, char** argv) | |||
| 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); | |||
| @@ -36,7 +37,9 @@ main(int argc, char** argv) | |||
| 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; | |||
| @@ -21,8 +21,23 @@ | |||
| #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; | |||
| } | |||
| @@ -28,6 +28,7 @@ main(int argc, char** argv) | |||
| 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); | |||
| @@ -37,6 +38,7 @@ main(int argc, char** argv) | |||
| LilvInstance* instance = lilv_plugin_instantiate(plugin, 48000.0, NULL); | |||
| TEST_ASSERT(!instance); | |||
| lilv_node_free(plugin_uri); | |||
| lilv_world_free(world); | |||
| return 0; | |||
| @@ -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> . | |||
| @@ -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; | |||
| } | |||
| @@ -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" | |||
| ] . | |||
| @@ -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; | |||
| } | |||
| @@ -28,6 +28,7 @@ main(int argc, char** argv) | |||
| 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); | |||
| @@ -38,7 +39,9 @@ main(int argc, char** argv) | |||
| 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; | |||
| @@ -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> . | |||
| @@ -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; | |||
| } | |||
| @@ -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" | |||
| ] . | |||
| @@ -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> . | |||