Hello community, here is the log from the commit of package lilv for openSUSE:Factory checked in at 2013-09-13 16:14:16 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/lilv (Old) and /work/SRC/openSUSE:Factory/.lilv.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "lilv" Changes: -------- --- /work/SRC/openSUSE:Factory/lilv/lilv.changes 2012-08-31 09:40:46.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.lilv.new/lilv.changes 2013-09-13 16:14:17.000000000 +0200 @@ -1,0 +2,26 @@ +Thu Sep 5 21:06:32 UTC 2013 - [email protected] + +- Update to 0.16.0 + * Add lilv_world_ask() for easily checking if a statement exists + * Add lilv_world_get() and lilv_port_get() for easily getting one value + * Add lilv_nodes_merge() + * Make lilv_plugin_get_port_by_designation() return a const pointer + * Require a URI for lilv_state_to_string() and fail gracefully otherwise + * Fail gracefully when lilv_state_new_from_string() is called on NULL + * Make state loading functions fall back to lv2:default for port values, + so a plugin description can be loaded as default state + * Ignore state ports with no value instead of printing an error + * Support atom:supports in lilv_port_supports_event() + * Add va_list variant of lilv_plugin_get_num_ports_of_class() + * Fix several plugin functions that failed to load data if called first + * Correctly depend on serd at build time (fix compilation in odd cases) + * Disable timestamps in HTML documentation for reproducible build + * lilvmm.hpp: Support varargs for Plugin::get_num_ports_of_class() + * lilvmm.hpp: Add several missing methods + * Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory) +- Remove lilv-0.14.4-build_compare.patch, applied upstream +- Add lilv-0.16.0-python_bindings.patch +- The minimum sord version is now 0.12.0 +- The documentation directory is now versioned + +------------------------------------------------------------------- Old: ---- lilv-0.14.4-build_compare.patch lilv-0.14.4.tar.bz2 New: ---- lilv-0.16.0-python_bindings.patch lilv-0.16.0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ lilv.spec ++++++ --- /var/tmp/diff_new_pack.9IUWOd/_old 2013-09-13 16:14:17.000000000 +0200 +++ /var/tmp/diff_new_pack.9IUWOd/_new 2013-09-13 16:14:17.000000000 +0200 @@ -1,7 +1,7 @@ # # spec file for package lilv # -# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,14 +19,15 @@ %define soname 0 Name: lilv -Version: 0.14.4 +Version: 0.16.0 Release: 0 Summary: A C library to make the use of LV2 plugins as simple as possible License: ISC Group: System/Libraries Url: http://drobilla.net/software/lilv/ Source0: http://download.drobilla.net/lilv-%{version}.tar.bz2 -Patch0: lilv-0.14.4-build_compare.patch +# From upstream SVN, remove with next version +Patch0: lilv-0.16.0-python_bindings.patch BuildRequires: doxygen BuildRequires: gcc-c++ BuildRequires: graphviz @@ -35,7 +36,7 @@ BuildRequires: swig BuildRequires: pkgconfig(lv2) >= 1.0.0 BuildRequires: pkgconfig(serd-0) >= 0.14.0 -BuildRequires: pkgconfig(sord-0) >= 0.8.0 +BuildRequires: pkgconfig(sord-0) >= 0.12.0 BuildRequires: pkgconfig(sratom-0) >= 0.2.0 %description @@ -110,7 +111,7 @@ %{_libdir}/liblilv-0.so %{_includedir}/lilv-0/ %{_libdir}/pkgconfig/lilv-0.pc -%{_defaultdocdir}/lilv/ +%{_defaultdocdir}/lilv-0/ %{_mandir}/man3/* %files -n python-lilv ++++++ lilv-0.16.0-python_bindings.patch ++++++ Index: lilv/lilvmm.hpp =================================================================== --- lilv/lilvmm.hpp (revision 5091) +++ lilv/lilvmm.hpp (revision 5092) @@ -49,6 +49,11 @@ return lilv_ ## prefix ## _ ## name(me, a1, a2); \ } +#define LILV_WRAP3(RT, prefix, name, T1, a1, T2, a2, T3, a3) \ + inline RT name(T1 a1, T2 a2, T3 a3) { \ + return lilv_ ## prefix ## _ ## name(me, a1, a2, a3); \ + } + #define LILV_WRAP2_VOID(prefix, name, T1, a1, T2, a2) \ inline void name(T1 a1, T2 a2) { lilv_ ## prefix ## _ ## name(me, a1, a2); } @@ -139,6 +144,27 @@ LILV_WRAP0(Node, nodes, get_first); }; +struct UI { + inline UI(const LilvUI* c_obj) : me(c_obj) {} + LILV_WRAP_CONVERSION(const LilvUI); + + LILV_WRAP0(const LilvNode*, ui, get_uri); + LILV_WRAP0(const LilvNode*, ui, get_bundle_uri); + LILV_WRAP0(const LilvNode*, ui, get_binary_uri); + LILV_WRAP0(const LilvNodes*, ui, get_classes); + /*LILV_WRAP3(bool, ui, is_supported, + LilvUISupportedFunc, supported_func, + const LilvNode*, container_type, + const LilvNode**, ui_type);*/ + LILV_WRAP1(bool, ui, is_a, const LilvNode*, class_uri); + + const LilvUI* me; +}; + +struct UIs { + LILV_WRAP_COLL(UIs, UI, uis); +}; + struct Port { inline Port(const LilvPlugin* p, const LilvPort* c_obj) : parent(p), me(c_obj) ++++++ lilv-0.14.4.tar.bz2 -> lilv-0.16.0.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/NEWS new/lilv-0.16.0/NEWS --- old/lilv-0.14.4/NEWS 2012-08-23 07:38:30.000000000 +0200 +++ new/lilv-0.16.0/NEWS 2013-02-18 22:49:59.000000000 +0100 @@ -1,3 +1,25 @@ +lilv (0.16.0) stable; + + * Add lilv_world_ask() for easily checking if a statement exists + * Add lilv_world_get() and lilv_port_get() for easily getting one value + * Add lilv_nodes_merge() + * Make lilv_plugin_get_port_by_designation() return a const pointer + * Require a URI for lilv_state_to_string() and fail gracefully otherwise + * Fail gracefully when lilv_state_new_from_string() is called on NULL + * Make state loading functions fall back to lv2:default for port values, + so a plugin description can be loaded as default state + * Ignore state ports with no value instead of printing an error + * Support atom:supports in lilv_port_supports_event() + * Add va_list variant of lilv_plugin_get_num_ports_of_class() + * Fix several plugin functions that failed to load data if called first + * Correctly depend on serd at build time (fix compilation in odd cases) + * Disable timestamps in HTML documentation for reproducible build + * lilvmm.hpp: Support varargs for Plugin::get_num_ports_of_class() + * lilvmm.hpp: Add several missing methods + * Update to waf 1.7.8 and autowaf r90 (install docs to versioned directory) + + -- David Robillard <[email protected]> Mon, 18 Feb 2013 16:43:10 -0500 + lilv (0.14.4) stable; * Deprecate old flawed Lilv::Instance constructors diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/doc/reference.doxygen.in new/lilv-0.16.0/doc/reference.doxygen.in --- old/lilv-0.14.4/doc/reference.doxygen.in 2012-08-04 21:43:44.000000000 +0200 +++ new/lilv-0.16.0/doc/reference.doxygen.in 2012-09-09 09:13:07.000000000 +0200 @@ -939,7 +939,7 @@ # 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. -HTML_TIMESTAMP = YES +HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/lilv/lilv.h new/lilv-0.16.0/lilv/lilv.h --- old/lilv-0.14.4/lilv/lilv.h 2012-06-30 21:24:41.000000000 +0200 +++ new/lilv-0.16.0/lilv/lilv.h 2013-02-10 07:01:15.000000000 +0100 @@ -21,6 +21,7 @@ #ifndef LILV_LILV_H #define LILV_LILV_H +#include <stdarg.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> @@ -464,6 +465,13 @@ bool lilv_nodes_contains(const LilvNodes* values, const LilvNode* value); +/** + Return a new LilvNodes that contains all nodes from both @p a and @p b. +*/ +LILV_API +LilvNodes* +lilv_nodes_merge(const LilvNodes* a, const LilvNodes* b); + /* Plugins */ LILV_API @@ -642,6 +650,38 @@ const LilvNode* object); /** + Find a single node that matches a pattern. + Exactly one of @p subject, @p predicate, @p object must be NULL. + This function is equivalent to + lilv_nodes_get_first(lilv_world_find_nodes(...)) but simplifies the common + case of only wanting a single value. + @return the first matching node, or NULL if no matches are found. +*/ +LILV_API +LilvNode* +lilv_world_get(LilvWorld* world, + const LilvNode* subject, + const LilvNode* predicate, + const LilvNode* object); + +/** + Return true iff a statement matching a certain pattern exists. + + This is useful for checking if particular statement exists without having to + bother with collections and memory management. + + @param subject Subject of statement, or NULL for anything. + @param predicate Predicate (key) of statement, or NULL for anything. + @param object Object (value) of statement, or NULL for anything. +*/ +LILV_API +bool +lilv_world_ask(LilvWorld* world, + const LilvNode* subject, + const LilvNode* predicate, + const LilvNode* object); + +/** @} @name Plugin @{ @@ -858,6 +898,19 @@ lilv_plugin_get_num_ports_of_class(const LilvPlugin* p, const LilvNode* class_1, ...); +#ifndef SWIG +/** + Variant of lilv_plugin_get_num_ports_of_class() that takes a va_list. + + This function calls va_arg() on @p args but does not call va_end(). +*/ +LILV_API +uint32_t +lilv_plugin_get_num_ports_of_class_va(const LilvPlugin* p, + const LilvNode* class_1, + va_list args); +#endif + /** Return whether or not the plugin introduces (and reports) latency. The index of the latency port can be found with lilv_plugin_get_latency_port @@ -908,7 +961,7 @@ ports for a particular designation. */ LILV_API -LilvPort* +const LilvPort* lilv_plugin_get_port_by_designation(const LilvPlugin* plugin, const LilvNode* port_class, const LilvNode* designation); @@ -1020,6 +1073,19 @@ const LilvNode* predicate); /** + Get a single property value of a port. + + This is equivalent to lilv_nodes_get_first(lilv_port_get_value(...)) but is + simpler to use in the common case of only caring about one value. The + caller is responsible for freeing the returned node. +*/ +LILV_API +LilvNode* +lilv_port_get(const LilvPlugin* plugin, + const LilvPort* port, + const LilvNode* predicate); + +/** Return the LV2 port properties of a port. */ LILV_API @@ -1037,13 +1103,16 @@ const LilvNode* property_uri); /** - Return whether a port is an event port and supports a certain event type. + Return whether a port supports a certain event type. + + More precisely, this returns true iff the port has an atom:supports or an + ev:supportsEvent property with @p event_type as the value. */ LILV_API bool lilv_port_supports_event(const LilvPlugin* p, const LilvPort* port, - const LilvNode* event_uri); + const LilvNode* event_type); /** Get the index of a port. @@ -1138,6 +1207,8 @@ /** Load a state snapshot from @c world's RDF model. + This function can be used to load the default state of a plugin by passing + the plugin URI as the @p subject parameter. @param subject The subject of the state description (e.g. a preset URI). @return A new LilvState which must be freed with lilv_state_free(). */ @@ -1377,6 +1448,11 @@ /** Save state to a string. This function does not use the filesystem. + + @param uri URI for the state description (mandatory). + @param base_uri Base URI for serialisation. Unless you know what you are + doing, pass NULL for this, otherwise the state may not be restorable via + lilv_state_new_from_string(). */ LILV_API char* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/lilv/lilvmm.hpp new/lilv-0.16.0/lilv/lilvmm.hpp --- old/lilv-0.14.4/lilv/lilvmm.hpp 2012-07-18 03:42:43.000000000 +0200 +++ new/lilv-0.16.0/lilv/lilvmm.hpp 2013-01-14 01:37:52.000000000 +0100 @@ -136,6 +136,7 @@ struct Nodes { LILV_WRAP_COLL(Nodes, Node, nodes); LILV_WRAP1(bool, nodes, contains, const Node, node); + LILV_WRAP0(Node, nodes, get_first); }; struct Port { @@ -190,6 +191,9 @@ LILV_WRAP0(Node, plugin, get_author_email); LILV_WRAP0(Node, plugin, get_author_homepage); LILV_WRAP0(bool, plugin, is_replaced); + LILV_WRAP0(Nodes, plugin, get_extension_data); + LILV_WRAP0(UIs, plugin, get_uis); + LILV_WRAP1(Nodes, plugin, get_related, Node, type); inline Port get_port_by_index(unsigned index) { return Port(me, lilv_plugin_get_port_by_index(me, index)); @@ -206,10 +210,15 @@ me, min_values, max_values, def_values); } - inline unsigned get_num_ports_of_class(LilvNode* class_1, - LilvNode* class_2) { - // TODO: varargs - return lilv_plugin_get_num_ports_of_class(me, class_1, class_2, NULL); + inline unsigned get_num_ports_of_class(LilvNode* class_1, ...) { + va_list args; + va_start(args, class_1); + + const uint32_t count = lilv_plugin_get_num_ports_of_class_va( + me, class_1, args); + + va_end(args); + return count; } const LilvPlugin* me; @@ -299,6 +308,7 @@ LILV_WRAP0(const LilvPluginClass*, world, get_plugin_class); LILV_WRAP0(const LilvPluginClasses*, world, get_plugin_classes); LILV_WRAP0(const Plugins, world, get_all_plugins); + LILV_WRAP1(int, world, load_resource, const LilvNode*, resource); LilvWorld* me; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/lilv.pc.in new/lilv-0.16.0/lilv.pc.in --- old/lilv-0.14.4/lilv.pc.in 2012-02-26 21:25:08.000000000 +0100 +++ new/lilv-0.16.0/lilv.pc.in 2012-09-10 20:17:34.000000000 +0200 @@ -6,6 +6,6 @@ Name: Lilv Version: @LILV_VERSION@ Description: Simple C library for hosting LV2 plugins -Requires: lv2core @LILV_PKG_DEPS@ +Requires: lv2 @PKG_serd_0@ @PKG_sord_0@ @PKG_sratom_0@ Libs: -L${libdir} -l@LIB_LILV@ -ldl Cflags: -I${includedir}/lilv-@LILV_MAJOR_VERSION@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/collections.c new/lilv-0.16.0/src/collections.c --- old/lilv-0.14.4/src/collections.c 2012-08-10 05:33:38.000000000 +0200 +++ new/lilv-0.16.0/src/collections.c 2012-11-23 06:54:39.000000000 +0100 @@ -143,6 +143,25 @@ return false; } +LILV_API +LilvNodes* +lilv_nodes_merge(const LilvNodes* a, const LilvNodes* b) +{ + LilvNodes* result = lilv_nodes_new(); + + LILV_FOREACH(nodes, i, a) + zix_tree_insert((ZixTree*)result, + lilv_node_duplicate(lilv_nodes_get(a, i)), + NULL); + + LILV_FOREACH(nodes, i, b) + zix_tree_insert((ZixTree*)result, + lilv_node_duplicate(lilv_nodes_get(b, i)), + NULL); + + return result; +} + /* Iterator */ #define LILV_COLLECTION_IMPL(prefix, CT, ET) \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/lilv_internal.h new/lilv-0.16.0/src/lilv_internal.h --- old/lilv-0.14.4/src/lilv_internal.h 2012-08-10 05:47:16.000000000 +0200 +++ new/lilv-0.16.0/src/lilv_internal.h 2012-11-23 07:00:15.000000000 +0100 @@ -310,11 +310,17 @@ const SordNode* predicate, const SordNode* object); +bool +lilv_world_ask_internal(LilvWorld* world, + const SordNode* subject, + const SordNode* predicate, + const SordNode* object); + LilvNodes* -lilv_world_query_values_internal(LilvWorld* world, - const SordNode* subject, - const SordNode* predicate, - const SordNode* object); +lilv_world_find_nodes_internal(LilvWorld* world, + const SordNode* subject, + const SordNode* predicate, + const SordNode* object); #define FOREACH_MATCH(iter) \ for (; !sord_iter_end(iter); sord_iter_next(iter)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/node.c new/lilv-0.16.0/src/node.c --- old/lilv-0.14.4/src/node.c 2012-08-10 05:56:01.000000000 +0200 +++ new/lilv-0.16.0/src/node.c 2013-02-10 07:14:02.000000000 +0100 @@ -92,6 +92,10 @@ LilvNode* lilv_node_new_from_node(LilvWorld* world, const SordNode* node) { + if (!node) { + return NULL; + } + LilvNode* result = NULL; SordNode* datatype_uri = NULL; LilvNodeType type = LILV_VALUE_STRING; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/plugin.c new/lilv-0.16.0/src/plugin.c --- old/lilv-0.14.4/src/plugin.c 2012-08-10 05:41:38.000000000 +0200 +++ new/lilv-0.16.0/src/plugin.c 2013-01-15 17:51:50.000000000 +0100 @@ -405,7 +405,7 @@ const SordNode* predicate) { lilv_plugin_load_if_necessary(p); - return lilv_world_query_values_internal( + return lilv_world_find_nodes_internal( p->world, p->plugin_uri->node, predicate, NULL); } @@ -528,37 +528,57 @@ LILV_API uint32_t -lilv_plugin_get_num_ports_of_class(const LilvPlugin* p, - const LilvNode* class_1, ...) +lilv_plugin_get_num_ports_of_class_va(const LilvPlugin* p, + const LilvNode* class_1, + va_list args) { lilv_plugin_load_ports_if_necessary(p); - uint32_t ret = 0; - va_list args; + uint32_t count = 0; + // Build array of classes from args so we can walk it several times + size_t n_classes = 0; + const LilvNode** classes = NULL; + for (LilvNode* class_i = NULL; (class_i = va_arg(args, LilvNode*)); ) { + classes = (const LilvNode**)realloc( + classes, ++n_classes * sizeof(LilvNode*)); + classes[n_classes - 1] = class_i; + } + + // Check each port against every type for (unsigned i = 0; i < p->num_ports; ++i) { LilvPort* port = p->ports[i]; - if (!port || !lilv_port_is_a(p, port, class_1)) - continue; - - va_start(args, class_1); + if (port && lilv_port_is_a(p, port, class_1)) { + bool matches = true; + for (size_t j = 0; j < n_classes; ++j) { + if (!lilv_port_is_a(p, port, classes[j])) { + matches = false; + break; + } + } - bool matches = true; - for (LilvNode* class_i = NULL; (class_i = va_arg(args, LilvNode*)); ) { - if (!lilv_port_is_a(p, port, class_i)) { - va_end(args); - matches = false; - break; + if (matches) { + ++count; } } + } + + free(classes); + return count; +} - if (matches) - ++ret; +LILV_API +uint32_t +lilv_plugin_get_num_ports_of_class(const LilvPlugin* p, + const LilvNode* class_1, ...) +{ + va_list args; + va_start(args, class_1); - va_end(args); - } + uint32_t count = lilv_plugin_get_num_ports_of_class_va(p, class_1, args); - return ret; + va_end(args); + return count; } LILV_API @@ -592,7 +612,7 @@ return ret; } -static LilvPort* +static const LilvPort* lilv_plugin_get_port_by_property(const LilvPlugin* plugin, const LilvNode* port_property) { @@ -617,7 +637,7 @@ } LILV_API -LilvPort* +const LilvPort* lilv_plugin_get_port_by_designation(const LilvPlugin* plugin, const LilvNode* port_class, const LilvNode* designation) @@ -648,10 +668,10 @@ uint32_t lilv_plugin_get_latency_port_index(const LilvPlugin* p) { - LilvNode* property = lilv_node_new_from_node( + LilvNode* prop = lilv_node_new_from_node( p->world, p->world->uris.lv2_reportsLatency); - LilvPort* latency_port = lilv_plugin_get_port_by_property(p, property); - lilv_node_free(property); + const LilvPort* latency_port = lilv_plugin_get_port_by_property(p, prop); + lilv_node_free(prop); return latency_port ? latency_port->index : UINT32_MAX; } @@ -660,13 +680,18 @@ lilv_plugin_has_feature(const LilvPlugin* p, const LilvNode* feature) { - LilvNodes* features = lilv_plugin_get_supported_features(p); - - const bool ret = (features && feature - && lilv_nodes_contains(features, feature)); - - lilv_nodes_free(features); - return ret; + lilv_plugin_load_if_necessary(p); + const SordNode* predicates[] = { p->world->uris.lv2_requiredFeature, + p->world->uris.lv2_optionalFeature, + NULL }; + + for (const SordNode** pred = predicates; *pred; ++pred) { + if (lilv_world_ask_internal( + p->world, p->plugin_uri->node, *pred, feature->node)) { + return true; + } + } + return false; } LILV_API @@ -675,20 +700,9 @@ { LilvNodes* optional = lilv_plugin_get_optional_features(p); LilvNodes* required = lilv_plugin_get_required_features(p); - LilvNodes* result = lilv_nodes_new(); - - LILV_FOREACH(nodes, i, optional) - zix_tree_insert((ZixTree*)result, - lilv_node_duplicate(lilv_nodes_get(optional, i)), - NULL); - LILV_FOREACH(nodes, i, required) - zix_tree_insert((ZixTree*)result, - lilv_node_duplicate(lilv_nodes_get(required, i)), - NULL); - + LilvNodes* result = lilv_nodes_merge(optional, required); lilv_nodes_free(optional); lilv_nodes_free(required); - return result; } @@ -696,16 +710,22 @@ LilvNodes* lilv_plugin_get_optional_features(const LilvPlugin* p) { - return lilv_plugin_get_value_internal( - p, p->world->uris.lv2_optionalFeature); + lilv_plugin_load_if_necessary(p); + return lilv_world_find_nodes_internal(p->world, + p->plugin_uri->node, + p->world->uris.lv2_optionalFeature, + NULL); } LILV_API LilvNodes* lilv_plugin_get_required_features(const LilvPlugin* p) { - return lilv_plugin_get_value_internal( - p, p->world->uris.lv2_requiredFeature); + lilv_plugin_load_if_necessary(p); + return lilv_world_find_nodes_internal(p->world, + p->plugin_uri->node, + p->world->uris.lv2_requiredFeature, + NULL); } LILV_API @@ -719,18 +739,12 @@ return false; } - SordIter* iter = lilv_world_query_internal( + lilv_plugin_load_if_necessary(p); + return lilv_world_ask_internal( p->world, p->plugin_uri->node, p->world->uris.lv2_extensionData, uri->node); - - if (iter) { - sord_iter_free(iter); - return true; - } else { - return false; - } } LILV_API @@ -937,8 +951,10 @@ LilvNodes* lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type) { + lilv_plugin_load_if_necessary(plugin); + LilvWorld* const world = plugin->world; - LilvNodes* const related = lilv_world_query_values_internal( + LilvNodes* const related = lilv_world_find_nodes_internal( world, NULL, world->uris.lv2_appliesTo, @@ -951,14 +967,12 @@ LilvNodes* matches = lilv_nodes_new(); LILV_FOREACH(nodes, i, related) { LilvNode* node = (LilvNode*)lilv_collection_get((ZixTree*)related, i); - SordIter* titer = lilv_world_query_internal( - world, node->node, world->uris.rdf_a, type->node); - if (!sord_iter_end(titer)) { + if (lilv_world_ask_internal( + world, node->node, world->uris.rdf_a, type->node)) { zix_tree_insert((ZixTree*)matches, lilv_node_new_from_node(world, node->node), NULL); } - sord_iter_free(titer); } lilv_nodes_free(related); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/port.c new/lilv-0.16.0/src/port.c --- old/lilv-0.14.4/src/port.c 2012-08-10 05:36:36.000000000 +0200 +++ new/lilv-0.16.0/src/port.c 2013-02-10 07:04:37.000000000 +0100 @@ -20,6 +20,7 @@ #include <stdlib.h> #include <string.h> +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" #include "lv2/lv2plug.in/ns/ext/event/event.h" #include "lilv_internal.h" @@ -68,16 +69,11 @@ const LilvPort* port, const LilvNode* property) { - assert(property); - SordIter* results = lilv_world_query_internal( + return lilv_world_ask_internal( p->world, port->node, p->world->uris.lv2_portProperty, lilv_node_as_node(property)); - - const bool ret = !sord_iter_end(results); - sord_iter_free(results); - return ret; } LILV_API @@ -86,16 +82,19 @@ const LilvPort* port, const LilvNode* event) { - assert(event); - SordIter* results = lilv_world_query_internal( - p->world, - port->node, - sord_new_uri(p->world->world, (const uint8_t*)LV2_EVENT__supportsEvent), - lilv_node_as_node(event)); - - const bool ret = !sord_iter_end(results); - sord_iter_free(results); - return ret; + const uint8_t* predicates[] = { (const uint8_t*)LV2_EVENT__supportsEvent, + (const uint8_t*)LV2_ATOM__supports, + NULL }; + + for (const uint8_t** pred = predicates; *pred; ++pred) { + if (lilv_world_ask_internal(p->world, + port->node, + sord_new_uri(p->world->world, *pred), + lilv_node_as_node(event))) { + return true; + } + } + return false; } static LilvNodes* @@ -103,15 +102,11 @@ const LilvPort* port, const SordNode* predicate) { - assert(sord_node_get_type(predicate) == SORD_URI); - - SordIter* results = lilv_world_query_internal( + return lilv_world_find_nodes_internal( p->world, port->node, predicate, NULL); - - return lilv_nodes_from_stream_objects(p->world, results, SORD_OBJECT); } LILV_API @@ -132,6 +127,21 @@ } LILV_API +LilvNode* +lilv_port_get(const LilvPlugin* p, + const LilvPort* port, + const LilvNode* predicate) +{ + LilvNodes* values = lilv_port_get_value(p, port, predicate); + + LilvNode* value = lilv_node_duplicate( + values ? lilv_nodes_get_first(values) : NULL); + + lilv_nodes_free(values); + return value; +} + +LILV_API uint32_t lilv_port_get_index(const LilvPlugin* p, const LilvPort* port) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/state.c new/lilv-0.16.0/src/state.c --- old/lilv-0.14.4/src/state.c 2012-08-13 04:01:15.000000000 +0200 +++ new/lilv-0.16.0/src/state.c 2013-02-18 19:02:47.000000000 +0100 @@ -432,15 +432,6 @@ } } -static SordNode* -get1(SordModel* model, const SordNode* s, const SordNode* p) -{ - SordIter* const i = sord_search(model, s, p, 0, 0); - SordNode* const node = i ? sord_node_copy(sord_iter_get_node(i, SORD_OBJECT)) : NULL; - sord_iter_free(i); - return node; -} - static LilvState* new_state_from_model(LilvWorld* world, LV2_URID_Map* map, @@ -463,6 +454,12 @@ state->dir = lilv_strdup((const char*)sord_node_get_string(graph)); } sord_iter_free(i); + } else if (sord_ask(model, + node, + world->uris.rdf_a, + world->uris.lv2_Plugin, 0)) { + // Loading plugin description as state (default state) + state->plugin_uri = lilv_node_new_from_node(world, node); } else { LILV_ERRORF("State %s missing lv2:appliesTo property\n", sord_node_get_string(node)); @@ -490,18 +487,18 @@ // Get port values SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0); FOREACH_MATCH(ports) { - const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT); - const SordNode* label = get1(model, port, world->uris.rdfs_label); - const SordNode* symbol = get1(model, port, world->uris.lv2_symbol); - const SordNode* value = get1(model, port, world->uris.pset_value); + const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT); + + SordNode* label = sord_get(model, port, world->uris.rdfs_label, 0, 0); + SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0); + SordNode* value = sord_get(model, port, world->uris.pset_value, 0, 0); + if (!value) { + value = sord_get(model, port, world->uris.lv2_default, 0, 0); + } if (!symbol) { LILV_ERRORF("State `%s' port missing symbol.\n", sord_node_get_string(node)); - } else if (!value) { - LILV_ERRORF("State `%s' port `%s' missing value.\n", - sord_node_get_string(symbol), - sord_node_get_string(node)); - } else { + } else if (value) { chunk.len = 0; sratom_read(sratom, &forge, world->world, model, value); LV2_Atom* atom = (LV2_Atom*)chunk.buf; @@ -515,12 +512,15 @@ (const char*)sord_node_get_string(label)); } } + sord_node_free(world->world, value); + sord_node_free(world->world, symbol); + sord_node_free(world->world, label); } sord_iter_free(ports); // Get properties - SordNode* statep = sord_new_uri(world->world, USTR(LV2_STATE__state)); - const SordNode* state_node = get1(model, node, statep); + SordNode* statep = sord_new_uri(world->world, USTR(LV2_STATE__state)); + SordNode* state_node = sord_get(model, node, statep, NULL, NULL); if (state_node) { SordIter* props = sord_search(model, state_node, 0, 0, 0); FOREACH_MATCH(props) { @@ -553,6 +553,7 @@ } sord_iter_free(props); } + sord_node_free(world->world, state_node); sord_node_free(world->world, statep); free((void*)chunk.buf); @@ -642,6 +643,10 @@ LV2_URID_Map* map, const char* str) { + if (!str) { + return NULL; + } + SerdNode base = SERD_NODE_NULL; SerdEnv* env = serd_env_new(&base); SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false); @@ -650,14 +655,13 @@ set_prefixes(env); serd_reader_read_string(reader, USTR(str)); - const SordNode* p = sord_new_uri(world->world, USTR(LILV_NS_RDF "type")); - const SordNode* o = sord_new_uri(world->world, USTR(LV2_PRESETS__Preset)); - SordIter* const i = sord_search(model, NULL, p, o, NULL); - const SordNode* s = sord_iter_get_node(i, SORD_SUBJECT); + SordNode* o = sord_new_uri(world->world, USTR(LV2_PRESETS__Preset)); + SordNode* s = sord_get(model, NULL, world->uris.rdf_a, o, NULL); LilvState* state = new_state_from_model(world, map, model, s, NULL); - sord_iter_free(i); + sord_node_free(world->world, s); + sord_node_free(world->world, o); serd_reader_free(reader); sord_free(model); serd_env_free(env); @@ -984,6 +988,11 @@ const char* uri, const char* base_uri) { + if (!uri) { + LILV_ERROR("Attempt to serialise state with no URI\n"); + return NULL; + } + SerdChunk chunk = { NULL, 0 }; SerdEnv* env = NULL; SerdNode base = serd_node_from_string(SERD_URI, USTR(base_uri)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/src/world.c new/lilv-0.16.0/src/world.c --- old/lilv-0.14.4/src/world.c 2012-08-10 05:41:56.000000000 +0200 +++ new/lilv-0.16.0/src/world.c 2013-02-18 19:02:23.000000000 +0100 @@ -185,31 +185,37 @@ LILV_ERRORF("Subject `%s' is not a resource\n", sord_node_get_string(subject->node)); return NULL; - } - if (!lilv_node_is_uri(predicate)) { + } else if (!lilv_node_is_uri(predicate)) { LILV_ERRORF("Predicate `%s' is not a URI\n", sord_node_get_string(predicate->node)); return NULL; - } - if (!subject && !object) { + } else if (!subject && !object) { LILV_ERROR("Both subject and object are NULL\n"); return NULL; } + + return lilv_world_find_nodes_internal( + world, + subject ? subject->node : NULL, + predicate->node, + object ? object->node : NULL); +} - SordNode* const subject_node = subject - ? sord_node_copy(subject->node) - : NULL; - - SordNode* const object_node = object - ? sord_node_copy(object->node) - : NULL; - - LilvNodes* ret = lilv_world_query_values_internal( - world, subject_node, predicate->node, object_node); - - sord_node_free(world->world, subject_node); - sord_node_free(world->world, object_node); - return ret; +LILV_API +LilvNode* +lilv_world_get(LilvWorld* world, + const LilvNode* subject, + const LilvNode* predicate, + const LilvNode* object) +{ + SordNode* snode = sord_get(world->model, + subject ? subject->node : NULL, + predicate ? predicate->node : NULL, + object ? object->node : NULL, + NULL); + LilvNode* lnode = lilv_node_new_from_node(world, snode); + sord_node_free(world->world, snode); + return lnode; } SordIter* @@ -221,11 +227,31 @@ return sord_search(world->model, subject, predicate, object, NULL); } +bool +lilv_world_ask_internal(LilvWorld* world, + const SordNode* subject, + const SordNode* predicate, + const SordNode* object) +{ + return sord_ask(world->model, subject, predicate, object, NULL); +} + +LILV_API +bool +lilv_world_ask(LilvWorld* world, + const LilvNode* subject, + const LilvNode* predicate, + const LilvNode* object) +{ + return sord_ask( + world->model, subject->node, predicate->node, object->node, NULL); +} + LilvNodes* -lilv_world_query_values_internal(LilvWorld* world, - const SordNode* subject, - const SordNode* predicate, - const SordNode* object) +lilv_world_find_nodes_internal(LilvWorld* world, + const SordNode* subject, + const SordNode* predicate, + const SordNode* object) { return lilv_nodes_from_stream_objects( world, @@ -667,49 +693,28 @@ FOREACH_MATCH(classes) { const SordNode* class_node = sord_iter_get_node(classes, SORD_SUBJECT); - // Get parents (superclasses) - SordIter* parents = sord_search( - world->model, - class_node, - world->uris.rdfs_subClassOf, - NULL, - NULL); - - if (sord_iter_end(parents)) { - sord_iter_free(parents); + SordNode* parent = sord_get( + world->model, class_node, world->uris.rdfs_subClassOf, NULL, NULL); + if (!parent || sord_node_get_type(parent) != SORD_URI) { continue; } - const SordNode* parent_node = sord_iter_get_node(parents, SORD_OBJECT); - sord_iter_free(parents); - - if (!sord_node_get_type(parent_node) == SORD_URI) { - // Class parent is not a resource, ignore (e.g. owl restriction) + SordNode* label = sord_get( + world->model, class_node, world->uris.rdfs_label, NULL, NULL); + if (!label) { + sord_node_free(world->world, parent); continue; } - // Get labels - SordIter* labels = sord_search( - world->model, - class_node, - world->uris.rdfs_label, - NULL, - NULL); - - if (sord_iter_end(labels)) { - sord_iter_free(labels); - continue; - } - - const SordNode* label_node = sord_iter_get_node(labels, SORD_OBJECT); - const uint8_t* label = sord_node_get_string(label_node); - sord_iter_free(labels); - LilvPluginClass* pclass = lilv_plugin_class_new( - world, parent_node, class_node, (const char*)label); + world, parent, class_node, + (const char*)sord_node_get_string(label)); if (pclass) { zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL); } + + sord_node_free(world->world, label); + sord_node_free(world->world, parent); } sord_iter_free(classes); } @@ -730,19 +735,14 @@ (ZixTree*)world->plugins, p); // ?new dc:replaces plugin - SordIter* replacement = sord_search( - world->model, - NULL, - world->uris.dc_replaces, - lilv_node_as_node(lilv_plugin_get_uri(plugin)), - NULL); - if (!sord_iter_end(replacement)) { - /* TODO: Check if replacement is actually a known plugin, - though this is expensive... - */ + if (sord_ask(world->model, + NULL, + world->uris.dc_replaces, + lilv_node_as_node(lilv_plugin_get_uri(plugin)), + NULL)) { + // TODO: Check if replacement is a known plugin? (expensive) ((LilvPlugin*)plugin)->replaced = true; } - sord_iter_free(replacement); } // Query out things to cache diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/test/lilv_test.c new/lilv-0.16.0/test/lilv_test.c --- old/lilv-0.14.4/test/lilv_test.c 2012-08-13 04:08:15.000000000 +0200 +++ new/lilv-0.16.0/test/lilv_test.c 2013-02-10 09:52:57.000000000 +0100 @@ -170,6 +170,7 @@ TestFunc func; }; +#define PREFIX_ATOM "@prefix atom: <http://lv2plug.in/ns/ext/atom#> . \n" #define PREFIX_LINE "@prefix : <http://example.org/> .\n" #define PREFIX_LV2 "@prefix lv2: <http://lv2plug.in/ns/lv2core#> .\n" #define PREFIX_LV2EV "@prefix lv2ev: <http://lv2plug.in/ns/ext/event#> . \n" @@ -180,7 +181,7 @@ #define PREFIX_DOAP "@prefix doap: <http://usefulinc.com/ns/doap#> .\n" #define MANIFEST_PREFIXES PREFIX_LINE PREFIX_LV2 PREFIX_RDFS -#define BUNDLE_PREFIXES PREFIX_LINE PREFIX_LV2 PREFIX_RDF PREFIX_RDFS PREFIX_FOAF PREFIX_DOAP +#define BUNDLE_PREFIXES PREFIX_ATOM PREFIX_LINE PREFIX_LV2 PREFIX_RDF PREFIX_RDFS PREFIX_FOAF PREFIX_DOAP #define PLUGIN_NAME(name) "doap:name \"" name "\"" #define LICENSE_GPL "doap:license <http://usefulinc.com/doap/licenses/gpl>" @@ -561,6 +562,14 @@ TEST_ASSERT(!strcmp(lilv_node_as_string(klass_uri), "http://lv2plug.in/ns/lv2core#CompressorPlugin")); + LilvNode* rdf_type = lilv_new_uri( + world, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"); + TEST_ASSERT(lilv_world_ask(world, + lilv_plugin_get_uri(plug), + rdf_type, + klass_uri)); + lilv_node_free(rdf_type); + const LilvNode* plug_bundle_uri = lilv_plugin_get_bundle_uri(plug); TEST_ASSERT(!strcmp(lilv_node_as_string(plug_bundle_uri), bundle_dir_uri)); @@ -616,7 +625,7 @@ LilvNode* lv2_latency = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#latency"); - LilvPort* latency_port = lilv_plugin_get_port_by_designation( + const LilvPort* latency_port = lilv_plugin_get_port_by_designation( plug, out_class, lv2_latency); lilv_node_free(lv2_latency); @@ -702,6 +711,8 @@ TEST_ASSERT(thing_name); TEST_ASSERT(lilv_node_is_string(thing_name)); TEST_ASSERT(!strcmp(lilv_node_as_string(thing_name), "Something else")); + LilvNode* thing_name2 = lilv_world_get(world, thing_uri, name_p, NULL); + TEST_ASSERT(lilv_node_equals(thing_name, thing_name2)); LilvUIs* uis = lilv_plugin_get_uis(plug); TEST_ASSERT(lilv_uis_size(uis) == 0); @@ -720,6 +731,7 @@ lilv_nodes_free(thing_names); lilv_node_free(thing_uri); + lilv_node_free(thing_name2); lilv_node_free(name_p); lilv_node_free(control_class); lilv_node_free(audio_class); @@ -756,7 +768,8 @@ " a lv2:EventPort ; a lv2:InputPort ; " " lv2:index 1 ; lv2:symbol \"event_in\" ; " " lv2:name \"Event Input\" ; " - " lv2ev:supportsEvent <http://example.org/event> " + " lv2ev:supportsEvent <http://example.org/event> ;" + " atom:supports <http://example.org/atomEvent> " "] .")) return 0; @@ -839,6 +852,9 @@ LilvNodes* comments = lilv_port_get_value(plug, p, rdfs_comment); TEST_ASSERT(!strcmp(lilv_node_as_string(lilv_nodes_get_first(comments)), "comment")); + LilvNode* comment = lilv_port_get(plug, p, rdfs_comment); + TEST_ASSERT(!strcmp(lilv_node_as_string(comment), "comment")); + lilv_node_free(comment); lilv_nodes_free(comments); setenv("LANG", "fr", 1); @@ -906,8 +922,10 @@ LilvNode* event_type = lilv_new_uri(world, "http://example.org/event"); LilvNode* event_type_2 = lilv_new_uri(world, "http://example.org/otherEvent"); + LilvNode* atom_event = lilv_new_uri(world, "http://example.org/atomEvent"); TEST_ASSERT(lilv_port_supports_event(plug, ep, event_type)); TEST_ASSERT(!lilv_port_supports_event(plug, ep, event_type_2)); + TEST_ASSERT(lilv_port_supports_event(plug, ep, atom_event)); LilvNode* name_p = lilv_new_uri(world, "http://lv2plug.in/ns/lv2core#name"); LilvNodes* names = lilv_port_get_value(plug, p, name_p); @@ -941,6 +959,7 @@ lilv_node_free(toggled_prop); lilv_node_free(event_type); lilv_node_free(event_type_2); + lilv_node_free(atom_event); lilv_node_free(min); lilv_node_free(max); @@ -1214,6 +1233,15 @@ // Ensure they are equal TEST_ASSERT(lilv_state_equals(state, state2)); + // Check that we can't save a state with no URI + char* bad_state_str = lilv_state_to_string( + world, &map, &unmap, state, NULL, NULL); + TEST_ASSERT(!bad_state_str); + + // Check that we can't restore the NULL string (and it doesn't crash) + LilvState* bad_state = lilv_state_new_from_string(world, &map, NULL); + TEST_ASSERT(!bad_state); + // Save state to a string char* state1_str = lilv_state_to_string( world, &map, &unmap, state, "http://example.org/state1", NULL); Files old/lilv-0.14.4/waf and new/lilv-0.16.0/waf differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lilv-0.14.4/wscript new/lilv-0.16.0/wscript --- old/lilv-0.14.4/wscript 2012-08-23 07:38:36.000000000 +0200 +++ new/lilv-0.16.0/wscript 2013-02-18 22:50:05.000000000 +0100 @@ -1,29 +1,22 @@ #!/usr/bin/env python import os -import sys import subprocess - -from waflib.extras import autowaf as autowaf +import sys import waflib.Options as Options +import waflib.extras.autowaf as autowaf -# Version of this package (even if built as a child) -LILV_VERSION = '0.14.4' -LILV_MAJOR_VERSION = '0' - -# Library version (UNIX style major, minor, micro) +# Library and package version (UNIX style major, minor, micro) # major increment <=> incompatible changes # minor increment <=> compatible changes (additions) # micro increment <=> no interface changes -# Lilv uses the same version number for both library and package -LILV_LIB_VERSION = LILV_VERSION +LILV_VERSION = '0.16.0' +LILV_MAJOR_VERSION = '0' -# Variables for 'waf dist' -APPNAME = 'lilv' -VERSION = LILV_VERSION - -# Mandatory variables -top = '.' -out = 'build' +# Mandatory waf variables +APPNAME = 'lilv' # Package name for waf dist +VERSION = LILV_VERSION # Package version for waf dist +top = '.' # Source directory +out = 'build' # Build directory def options(opt): opt.load('compiler_c') @@ -84,9 +77,9 @@ autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', atleast_version='0.14.0', mandatory=True) autowaf.check_pkg(conf, 'sord-0', uselib_store='SORD', - atleast_version='0.8.0', mandatory=True) + atleast_version='0.12.0', mandatory=True) autowaf.check_pkg(conf, 'sratom-0', uselib_store='SRATOM', - atleast_version='0.2.0', mandatory=True) + atleast_version='0.4.0', mandatory=True) autowaf.define(conf, 'LILV_NEW_LV2', 1) # New LV2 discovery API @@ -155,8 +148,7 @@ '/usr/local/%s/lv2' % libdirname]) autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', lv2_path) - conf.env.LIB_LILV = ['lilv-%s' % LILV_MAJOR_VERSION] - + autowaf.set_lib_env(conf, 'lilv', LILV_VERSION) conf.write_config_header('lilv_config.h', remove=False) autowaf.display_msg(conf, 'Default LV2_PATH', @@ -237,12 +229,12 @@ includes = ['.', './src'], name = 'liblilv', target = 'lilv-%s' % LILV_MAJOR_VERSION, - vnum = LILV_LIB_VERSION, + vnum = LILV_VERSION, install_path = '${LIBDIR}', defines = ['LILV_SHARED', 'LILV_INTERNAL'], cflags = libflags, lib = lib) - autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') + autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2') # Static library if bld.env.BUILD_STATIC: @@ -252,10 +244,10 @@ includes = ['.', './src'], name = 'liblilv_static', target = 'lilv-%s' % LILV_MAJOR_VERSION, - vnum = LILV_LIB_VERSION, + vnum = LILV_VERSION, install_path = '${LIBDIR}', defines = defines + ['LILV_INTERNAL']) - autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') + autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2') if bld.env.BUILD_TESTS: test_libs = lib @@ -301,7 +293,7 @@ defines = defines + ['LILV_INTERNAL'], cflags = test_cflags, lib = test_libs) - autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') + autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2') # Unit test program bpath = os.path.abspath(os.path.join(out, 'test', 'test_plugin.lv2')) @@ -310,13 +302,12 @@ source = 'test/lilv_test.c', includes = ['.', './src'], use = 'liblilv_profiled', - uselib = 'SORD LV2', lib = test_libs, target = 'test/lilv_test', install_path = None, defines = defines + ['LILV_TEST_BUNDLE=\"%s/\"' % bpath], cflags = test_cflags) - autowaf.use_lib(bld, obj, 'SORD SRATOM LV2') + autowaf.use_lib(bld, obj, 'SERD SORD SRATOM LV2') # Utilities if bld.env.BUILD_UTILS: -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
