summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugin.c26
-rw-r--r--src/port.c45
-rw-r--r--src/query.c119
-rw-r--r--src/slv2_internal.h11
-rw-r--r--src/util.c45
-rw-r--r--src/world.c11
6 files changed, 145 insertions, 112 deletions
diff --git a/src/plugin.c b/src/plugin.c
index cb3d630..180c82a 100644
--- a/src/plugin.c
+++ b/src/plugin.c
@@ -406,7 +406,7 @@ SLV2_API
SLV2Value
slv2_plugin_get_name(SLV2Plugin plugin)
{
- SLV2Values results = slv2_plugin_get_value_by_qname_i18n(plugin, "doap:name");
+ SLV2Values results = slv2_plugin_get_value_by_qname(plugin, "doap:name");
SLV2Value ret = NULL;
if (results) {
@@ -456,30 +456,6 @@ slv2_plugin_get_value_by_qname(SLV2Plugin p,
SLV2_API
SLV2Values
-slv2_plugin_get_value_by_qname_i18n(SLV2Plugin p,
- const char* predicate)
-{
- uint8_t* pred_uri = slv2_qname_expand(p, predicate);
- if (!pred_uri) {
- return NULL;
- }
-
- SLV2Node pred_node = sord_get_uri(
- p->world->model, true, (const uint8_t*)pred_uri);
-
- SLV2Matches results = slv2_plugin_find_statements(
- p,
- p->plugin_uri->val.uri_val,
- pred_node,
- NULL);
-
- slv2_node_free(pred_node);
- free(pred_uri);
- return slv2_values_from_stream_i18n(p, results);
-}
-
-SLV2_API
-SLV2Values
slv2_plugin_get_value_for_subject(SLV2Plugin p,
SLV2Value subject,
SLV2Value predicate)
diff --git a/src/port.c b/src/port.c
index 80fa421..2ee3337 100644
--- a/src/port.c
+++ b/src/port.c
@@ -130,26 +130,6 @@ slv2_port_supports_event(SLV2Plugin p,
return ret;
}
-static SLV2Values
-slv2_values_from_stream_objects(SLV2Plugin p, SLV2Matches stream)
-{
- if (slv2_matches_end(stream)) {
- slv2_match_end(stream);
- return NULL;
- }
-
- SLV2Values values = slv2_values_new();
- FOREACH_MATCH(stream) {
- g_ptr_array_add(
- values,
- slv2_value_new_from_node(
- p->world,
- slv2_match_object(stream)));
- }
- slv2_match_end(stream);
- return values;
-}
-
SLV2_API
SLV2Values
slv2_port_get_value_by_qname(SLV2Plugin p,
@@ -207,29 +187,6 @@ slv2_port_get_value(SLV2Plugin p,
}
SLV2_API
-SLV2Values
-slv2_port_get_value_by_qname_i18n(SLV2Plugin p,
- SLV2Port port,
- const char* predicate)
-{
- assert(predicate);
- uint8_t* pred_uri = slv2_qname_expand(p, predicate);
- if (!pred_uri) {
- return NULL;
- }
-
- SLV2Node port_node = slv2_port_get_node(p, port);
- SLV2Matches results = slv2_plugin_find_statements(
- p,
- port_node,
- sord_get_uri(p->world->model, true, pred_uri),
- NULL);
-
- free(pred_uri);
- return slv2_values_from_stream_i18n(p, results);
-}
-
-SLV2_API
SLV2Value
slv2_port_get_symbol(SLV2Plugin p,
SLV2Port port)
@@ -243,7 +200,7 @@ slv2_port_get_name(SLV2Plugin p,
SLV2Port port)
{
SLV2Value ret = NULL;
- SLV2Values results = slv2_port_get_value_by_qname_i18n(p, port, "lv2:name");
+ SLV2Values results = slv2_port_get_value_by_qname(p, port, "lv2:name");
if (results && slv2_values_size(results) > 0) {
ret = slv2_value_duplicate(slv2_values_get_at(results, 0));
diff --git a/src/query.c b/src/query.c
index 0acd0d0..9382fdc 100644
--- a/src/query.c
+++ b/src/query.c
@@ -38,41 +38,118 @@ slv2_plugin_find_statements(SLV2Plugin plugin,
return sord_find(plugin->world->model, pat);
}
+typedef enum {
+ SLV2_LANG_MATCH_NONE, ///< Language does not match at all
+ SLV2_LANG_MATCH_PARTIAL, ///< Partial (language, but not country) match
+ SLV2_LANG_MATCH_EXACT ///< Exact (language and country) match
+} SLV2LangMatch;
+
+static SLV2LangMatch
+slv2_lang_matches(const char* a, const char* b)
+{
+ if (!strcmp(a, b)) {
+ return SLV2_LANG_MATCH_EXACT;
+ }
+
+ const char* a_dash = strchr(a, '-');
+ const size_t a_lang_len = a_dash ? (a_dash - a) : 0;
+ const char* b_dash = strchr(b, '-');
+ const size_t b_lang_len = b_dash ? (b_dash - b) : 0;
+
+ if (a_lang_len && b_lang_len) {
+ if (a_lang_len == b_lang_len && !strncmp(a, b, a_lang_len)) {
+ return SLV2_LANG_MATCH_PARTIAL; // e.g. a="en-gb", b="en-ca"
+ }
+ } else if (a_lang_len && !strncmp(a, b, a_lang_len)) {
+ return SLV2_LANG_MATCH_PARTIAL; // e.g. a="en", b="en-ca"
+ } else if (b_lang_len && !strncmp(a, b, b_lang_len)) {
+ return SLV2_LANG_MATCH_PARTIAL; // e.g. a="en-ca", b="en"
+ }
+ return SLV2_LANG_MATCH_NONE;
+}
+
SLV2Values
-slv2_values_from_stream_i18n(SLV2Plugin p,
- SLV2Matches stream)
+slv2_values_from_stream_objects_i18n(SLV2Plugin p,
+ SLV2Matches stream)
{
- SLV2Values values = slv2_values_new();
- SLV2Node nolang = NULL;
+ SLV2Values values = slv2_values_new();
+ SLV2Node nolang = NULL; // Untranslated value
+ SLV2Node partial = NULL; // Partial language match
+ char* syslang = slv2_get_lang();
FOREACH_MATCH(stream) {
SLV2Node value = slv2_match_object(stream);
if (sord_node_get_type(value) == SORD_LITERAL) {
- const char* lang = sord_literal_get_lang(value);
+ const char* lang = sord_literal_get_lang(value);
+ SLV2LangMatch lm = SLV2_LANG_MATCH_NONE;
if (lang) {
- if (!strcmp(lang, slv2_get_lang())) {
- g_ptr_array_add(
- values, (uint8_t*)slv2_value_new_string(
- p->world, (const char*)sord_node_get_string(value)));
- }
+ lm = (syslang)
+ ? slv2_lang_matches(lang, syslang)
+ : SLV2_LANG_MATCH_PARTIAL;
} else {
nolang = value;
+ if (!syslang) {
+ lm = SLV2_LANG_MATCH_EXACT;
+ }
}
+
+ if (lm == SLV2_LANG_MATCH_EXACT) {
+ // Exact language match, add to results
+ g_ptr_array_add(values, slv2_value_new_from_node(p->world, value));
+ } else if (lm == SLV2_LANG_MATCH_PARTIAL) {
+ // Partial language match, save in case we find no exact
+ partial = value;
+ }
+ } else {
+ g_ptr_array_add(values, slv2_value_new_from_node(p->world, value));
}
- break;
}
slv2_match_end(stream);
+ free(syslang);
- if (slv2_values_size(values) == 0) {
- // No value with a matching language, use untranslated default
- if (nolang) {
- g_ptr_array_add(
- values, (uint8_t*)slv2_value_new_string(
- p->world, (const char*)sord_node_get_string(nolang)));
- } else {
- slv2_values_free(values);
- values = NULL;
- }
+ if (slv2_values_size(values) > 0) {
+ return values;
+ }
+
+ SLV2Node best = nolang;
+ if (syslang && partial) {
+ // Partial language match for system language
+ best = partial;
+ } else if (!best) {
+ // No languages matches at all, and no untranslated value
+ // Use any value, if possible
+ best = partial;
+ }
+
+ if (best) {
+ g_ptr_array_add(values, slv2_value_new_from_node(p->world, best));
+ } else {
+ // No matches whatsoever
+ slv2_values_free(values);
+ values = NULL;
}
return values;
}
+
+SLV2Values
+slv2_values_from_stream_objects(SLV2Plugin p,
+ SLV2Matches stream)
+{
+ if (slv2_matches_end(stream)) {
+ slv2_match_end(stream);
+ return NULL;
+ } else if (p->world->filter_language) {
+ return slv2_values_from_stream_objects_i18n(p, stream);
+ } else {
+ SLV2Values values = slv2_values_new();
+ FOREACH_MATCH(stream) {
+ g_ptr_array_add(
+ values,
+ slv2_value_new_from_node(
+ p->world,
+ slv2_match_object(stream)));
+ }
+ slv2_match_end(stream);
+ return values;
+ }
+}
diff --git a/src/slv2_internal.h b/src/slv2_internal.h
index 7cf8d11..12a32a0 100644
--- a/src/slv2_internal.h
+++ b/src/slv2_internal.h
@@ -202,6 +202,7 @@ struct _SLV2World {
SLV2Node slv2_dmanifest_node;
SLV2Node xsd_integer_node;
SLV2Node xsd_decimal_node;
+ bool filter_language;
};
const uint8_t*
@@ -290,15 +291,15 @@ static inline bool slv2_matches_end(SLV2Matches matches) {
return sord_iter_end(matches);
}
-SLV2Values slv2_values_from_stream_i18n(SLV2Plugin p,
- SLV2Matches stream);
+SLV2Values slv2_values_from_stream_objects(SLV2Plugin p,
+ SLV2Matches stream);
/* ********* Utilities ********* */
-char* slv2_strjoin(const char* first, ...);
-const char* slv2_get_lang();
-uint8_t* slv2_qname_expand(SLV2Plugin p, const char* qname);
+char* slv2_strjoin(const char* first, ...);
+char* slv2_get_lang();
+uint8_t* slv2_qname_expand(SLV2Plugin p, const char* qname);
typedef void (*VoidFunc)();
diff --git a/src/util.c b/src/util.c
index 8024f2e..bcb5e6c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -62,24 +62,37 @@ slv2_uri_to_path(const char* uri)
return NULL;
}
-const char*
+/** 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*
slv2_get_lang()
{
- static char lang[32];
- lang[31] = '\0';
- char* tmp = getenv("LANG");
- if (!tmp) {
- lang[0] = '\0';
- } else {
- strncpy(lang, tmp, 31);
- for (int i = 0; i < 31 && lang[i]; ++i) {
- if (lang[i] == '_') {
- lang[i] = '-';
- } else if ( !(lang[i] >= 'a' && lang[i] <= 'z')
- && !(lang[i] >= 'A' && lang[i] <= 'Z')) {
- lang[i] = '\0';
- break;
- }
+ 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 = 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 {
+ SLV2_ERRORF("Illegal LANG `%s' ignored\n", env_lang);
+ free(lang);
+ return NULL;
}
}
diff --git a/src/world.c b/src/world.c
index 4c612ee..ae04b17 100644
--- a/src/world.c
+++ b/src/world.c
@@ -93,7 +93,8 @@ slv2_world_new()
slv2_world_set_prefix(world, "lv2", "http://lv2plug.in/ns/lv2core#");
slv2_world_set_prefix(world, "lv2ev", "http://lv2plug.in/ns/ext/event#");
- world->n_read_files = 0;
+ world->n_read_files = 0;
+ world->filter_language = true;
return world;
@@ -148,6 +149,14 @@ slv2_world_free(SLV2World world)
free(world);
}
+SLV2_API
+void
+slv2_world_filter_language(SLV2World world, bool filter)
+{
+ world->filter_language = filter;
+}
+
+
static SLV2Matches
slv2_world_find_statements(SLV2World world,
Sord model,