diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/plugin.c | 26 | ||||
-rw-r--r-- | src/port.c | 45 | ||||
-rw-r--r-- | src/query.c | 119 | ||||
-rw-r--r-- | src/slv2_internal.h | 11 | ||||
-rw-r--r-- | src/util.c | 45 | ||||
-rw-r--r-- | src/world.c | 11 |
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) @@ -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)(); @@ -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, |