diff options
author | Akira TAGOH <akira@tagoh.org> | 2015-12-01 13:09:26 +0900 |
---|---|---|
committer | Akira TAGOH <akira@tagoh.org> | 2015-12-01 13:09:48 +0900 |
commit | 28caed6dd1440f19f7c8b4122ca55d1323bc82c7 (patch) | |
tree | bcd34540a121e96deef5070c4075842f8c9bb1a2 | |
parent | 284f3c271e37145eb94cb1d0b5b205aaddb50dcc (diff) |
Add a lookup table to catch up a script/language from language/script
-rw-r--r-- | data/Makefile.am | 5 | ||||
-rw-r--r-- | liblangtag-gobject/Makefile.am | 2 | ||||
-rw-r--r-- | liblangtag/Makefile.am | 2 | ||||
-rw-r--r-- | liblangtag/lt-database.c | 16 | ||||
-rw-r--r-- | liblangtag/lt-database.h | 4 | ||||
-rw-r--r-- | liblangtag/lt-macros.h | 26 | ||||
-rw-r--r-- | liblangtag/lt-relation-db.c | 283 | ||||
-rw-r--r-- | liblangtag/lt-relation-db.h | 43 | ||||
-rw-r--r-- | liblangtag/lt-xml.c | 41 | ||||
-rw-r--r-- | liblangtag/lt-xml.h | 5 | ||||
-rw-r--r-- | tests/tag.c | 16 |
11 files changed, 414 insertions, 29 deletions
diff --git a/data/Makefile.am b/data/Makefile.am index 84b9c5b..9ec3d34 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -42,8 +42,9 @@ bcp47_xml_files = \ common/bcp47/transform_private_use.xml \ common/bcp47/variant.xml \ $(NULL) -supplemental_xml_files = \ - common/supplemental/likelySubtags.xml \ +supplemental_xml_files = \ + common/supplemental/likelySubtags.xml \ + common/supplemental/supplementalData.xml \ $(NULL) stamp_files = \ stamp-core-zip \ diff --git a/liblangtag-gobject/Makefile.am b/liblangtag-gobject/Makefile.am index 0bc0dea..d932a0f 100644 --- a/liblangtag-gobject/Makefile.am +++ b/liblangtag-gobject/Makefile.am @@ -82,6 +82,7 @@ liblangtag_gobject_built_private_headers = \ lt-redundant.gir.h \ lt-region-db.gir.h \ lt-region.gir.h \ + lt-relation-db.gir.h \ lt-script-db.gir.h \ lt-script.gir.h \ lt-string.gir.h \ @@ -107,6 +108,7 @@ liblangtag_gobject_built_sources = \ lt-redundant.gir.c \ lt-region-db.gir.c \ lt-region.gir.c \ + lt-relation-db.gir.c \ lt-script-db.gir.c \ lt-script.gir.c \ lt-string.gir.c \ diff --git a/liblangtag/Makefile.am b/liblangtag/Makefile.am index 8bc788b..5431c8b 100644 --- a/liblangtag/Makefile.am +++ b/liblangtag/Makefile.am @@ -70,6 +70,7 @@ liblangtag_public_headers = \ lt-redundant-db.h \ lt-region.h \ lt-region-db.h \ + lt-relation-db.h \ lt-script.h \ lt-script-db.h \ lt-string.h \ @@ -127,6 +128,7 @@ liblangtag_sources = \ lt-redundant-db.c \ lt-region.c \ lt-region-db.c \ + lt-relation-db.c \ lt-script.c \ lt-script-db.c \ lt-string.c \ diff --git a/liblangtag/lt-database.c b/liblangtag/lt-database.c index 6dfd992..7f944ec 100644 --- a/liblangtag/lt-database.c +++ b/liblangtag/lt-database.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * lt-database.c - * Copyright (C) 2011-2012 Akira TAGOH + * Copyright (C) 2011-2015 Akira TAGOH * * Authors: * Akira TAGOH <akira@tagoh.org> @@ -36,6 +36,7 @@ static lt_region_db_t *__db_region = NULL; static lt_variant_db_t *__db_variant = NULL; static lt_grandfathered_db_t *__db_grandfathered = NULL; static lt_redundant_db_t *__db_redundant = NULL; +static lt_relation_db_t *__db_relation = NULL; static char __lt_db_datadir[LT_PATH_MAX] = { 0 }; @@ -103,6 +104,8 @@ lt_db_initialize(void) lt_db_get_grandfathered(); if (!__db_redundant) lt_db_get_redundant(); + if (!__db_relation) + lt_db_get_relation(); lt_ext_modules_load(); } @@ -122,6 +125,7 @@ lt_db_finalize(void) lt_variant_db_unref(__db_variant); lt_grandfathered_db_unref(__db_grandfathered); lt_redundant_db_unref(__db_redundant); + lt_relation_db_unref(__db_relation); lt_ext_modules_unload(); } @@ -210,3 +214,13 @@ DEFUNC_GET_INSTANCE(script) * Returns: The instance of #lt_variant_db_t. */ DEFUNC_GET_INSTANCE(variant) +/** + * lt_db_get_relation: + * + * Obtains the instance of #lt_relation_db_t. This still allows to use without + * lt_db_initialize(). but it will takes some time to load the database on + * the memory every time. + * + * Returns: The instance of #lt_relation_db_t. + */ +DEFUNC_GET_INSTANCE(relation) diff --git a/liblangtag/lt-database.h b/liblangtag/lt-database.h index 5c45368..310814b 100644 --- a/liblangtag/lt-database.h +++ b/liblangtag/lt-database.h @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * lt-database.h - * Copyright (C) 2011-2012 Akira TAGOH + * Copyright (C) 2011-2015 Akira TAGOH * * Authors: * Akira TAGOH <akira@tagoh.org> @@ -25,6 +25,7 @@ #include <liblangtag/lt-region-db.h> #include <liblangtag/lt-script-db.h> #include <liblangtag/lt-variant-db.h> +#include <liblangtag/lt-relation-db.h> LT_BEGIN_DECLS @@ -39,6 +40,7 @@ lt_region_db_t *lt_db_get_region (void); lt_variant_db_t *lt_db_get_variant (void); lt_grandfathered_db_t *lt_db_get_grandfathered(void); lt_redundant_db_t *lt_db_get_redundant (void); +lt_relation_db_t *lt_db_get_relation (void); LT_END_DECLS diff --git a/liblangtag/lt-macros.h b/liblangtag/lt-macros.h index 83bbe31..8c25915 100644 --- a/liblangtag/lt-macros.h +++ b/liblangtag/lt-macros.h @@ -120,11 +120,32 @@ * It allows the compiler to type-check the arguments passed to the function. * See the GNU C documentation for details. */ +/** + * LT_GNUC_UNUSED: + * + * Expands to the GNU C unused function attribute if the compiler is gcc. + * It is used for declaring functions and arguments which may never be used. + * It avoids possible compiler warnings. + * + * For functions, place the attribute after the declaration, just before the + * semicolon. For arguments, place the attribute at the beginning of the + * argument declaration. + * + * |[<!-- language="C" --> + * void my_unused_function (LT_GNUC_UNUSED int unused_argument, + * int other argument) LT_GNUC_UNUSED; + * ]| + * + * See the GNU C documentation for more details. + */ #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) # define LT_GNUC_PRINTF(format_idx, arg_idx) \ __attribute__((__format__ (__printf__, format_idx, arg_idx))) +# define LT_GNUC_UNUSED \ + __attribute__((__unused__)) #else /* !__GNUC__ */ # define LT_GNUC_PRINTF(format_idx, arg_idx) +# define LT_GNUC_UNUSED #endif /** * LT_GNUC_NULL_TERMINATED: @@ -304,6 +325,11 @@ LT_STMT_START {raise(SIGTRAP);} LT_STMT_END #endif +/* assertion */ +#define _LT_ASSERT_STATIC1(_l_,_x_) typedef int _static_assert_on_line_##_l_##_failed[(_x_)?1:-1] LT_GNUC_UNUSED +#define _LT_ASSERT_STATIC0(_l_,_x_) _LT_ASSERT_STATIC1 (_l_, (_x_)) +#define LT_ASSERT_STATIC(_x_) _LT_ASSERT_STATIC0 (__LINE__, (_x_)) + LT_BEGIN_DECLS #if defined(_MSC_VER) && !defined(ssize_t) diff --git a/liblangtag/lt-relation-db.c b/liblangtag/lt-relation-db.c new file mode 100644 index 0000000..3451032 --- /dev/null +++ b/liblangtag/lt-relation-db.c @@ -0,0 +1,283 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * lt-relation-db.c + * Copyright (C) 2015 Akira TAGOH + * + * Authors: + * Akira TAGOH <akira@tagoh.org> + * + * You may distribute under the terms of either the GNU + * Lesser General Public License or the Mozilla Public + * License, as specified in the README file. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <libxml/xpath.h> +#include "lt-database.h" +#include "lt-error.h" +#include "lt-list.h" +#include "lt-mem.h" +#include "lt-messages.h" +#include "lt-trie.h" +#include "lt-utils.h" +#include "lt-xml.h" +#include "lt-relation-db.h" + +/** + * SECTION:lt-relation-db + * @Short_Description: An interface to access relation database + * @Title: Database - Relation + * + * This class providdes an interface to access Relation database. + */ +struct _lt_relation_db_t { + lt_mem_t parent; + lt_xml_t *xml; + lt_trie_t *relation_l_s_entries; + lt_trie_t *relation_s_l_entries; +}; + +/*< private >*/ +static lt_bool_t +lt_relation_db_parse(lt_relation_db_t *relationdb, + lt_error_t **error) +{ + lt_bool_t retval = TRUE; + xmlDocPtr doc = NULL; + xmlXPathContextPtr xctxt = NULL; + xmlXPathObjectPtr xobj = NULL; + lt_error_t *err = NULL; + int i, n; + lt_lang_db_t *langdb; + lt_script_db_t *scriptdb; + + lt_return_val_if_fail (relationdb != NULL, FALSE); + + doc = lt_xml_get_cldr(relationdb->xml, LT_XML_CLDR_SUPPLEMENTAL_SUPPLEMENTAL_DATA); + xctxt = xmlXPathNewContext(doc); + if (!xctxt) { + lt_error_set(&err, LT_ERR_OOM, + "Unable to create an instance of xmlXPathContextPtr."); + goto bail; + } + xobj = xmlXPathEvalExpression((const xmlChar *)"/supplementalData/languageData/language[@scripts]", xctxt); + if (!xobj) { + lt_error_set(&err, LT_ERR_FAIL_ON_XML, + "No valid elements for %s", + doc->name); + goto bail; + } + n = xmlXPathNodeSetGetLength(xobj->nodesetval); + + langdb = lt_db_get_lang(); + scriptdb = lt_db_get_script(); + for (i = 0; i < n; i++) { + xmlNodePtr ent = xmlXPathNodeSetItem(xobj->nodesetval, i); + xmlChar *type, *scripts; + char *p, *pp; + lt_list_t *l; + lt_lang_t *ol; + lt_script_t *os; + lt_bool_t alloced; + + type = xmlGetProp(ent, (const xmlChar *)"type"); + scripts = xmlGetProp(ent, (const xmlChar *)"scripts"); + p = (char *)scripts; + do { + pp = strchr(p, ' '); + if (pp) { + *pp = 0; + pp++; + } + ol = lt_lang_db_lookup(langdb, (const char *)type); + os = lt_script_db_lookup(scriptdb, p); + if (!ol || !os) { + if (!ol) + lt_warning("Unknown lang tag: %s", type); + else + lt_lang_unref(ol); + if (!os) + lt_warning("Unknown script tag: %s", p); + else + lt_script_unref(os); + goto bail1; + } + alloced = FALSE; + l = lt_trie_lookup(relationdb->relation_l_s_entries, + (const char *)type); + if (!l) + alloced = TRUE; + l = lt_list_append(l, lt_script_ref(os), (lt_destroy_func_t)lt_script_unref); + if (alloced) { + lt_mem_add_ref((lt_mem_t *)relationdb->relation_l_s_entries, + l, (lt_destroy_func_t)lt_list_free); + } + lt_trie_replace(relationdb->relation_l_s_entries, + (const char *)type, l, NULL); + alloced = FALSE; + l = lt_trie_lookup(relationdb->relation_s_l_entries, p); + if (!l) + alloced = TRUE; + l = lt_list_append(l, lt_lang_ref(ol), (lt_destroy_func_t)lt_lang_unref); + if (alloced) { + lt_mem_add_ref((lt_mem_t *)relationdb->relation_s_l_entries, + l, (lt_destroy_func_t)lt_list_free); + } + lt_trie_replace(relationdb->relation_s_l_entries, p, l, NULL); + bail1: + p = pp; + } while (p); + + xmlFree(type); + xmlFree(scripts); + } + + bail: + if (lt_error_is_set(err, LT_ERR_ANY)) { + if (error) + *error = lt_error_ref(err); + else + lt_error_print(err, LT_ERR_ANY); + lt_error_unref(err); + retval = FALSE; + } + if (xobj) + xmlXPathFreeObject(xobj); + if (xctxt) + xmlXPathFreeContext(xctxt); + + return retval; +} + +/*< public >*/ +/** + * lt_relation_db_new: + * + * Create a new instance of a #lt_relation_db_t. + * + * Returns: (transfer full): a new instance of #lt_relation_db_t. + */ +lt_relation_db_t * +lt_relation_db_new(void) +{ + lt_relation_db_t *retval = lt_mem_alloc_object(sizeof (lt_relation_db_t)); + + if (retval) { + lt_error_t *err = NULL; + + retval->relation_l_s_entries = lt_trie_new(); + lt_mem_add_ref((lt_mem_t *)retval, retval->relation_l_s_entries, + (lt_destroy_func_t)lt_trie_unref); + retval->relation_s_l_entries = lt_trie_new(); + lt_mem_add_ref((lt_mem_t *)retval, retval->relation_s_l_entries, + (lt_destroy_func_t)lt_trie_unref); + retval->xml = lt_xml_new(); + if (!retval->xml) { + lt_relation_db_unref(retval); + retval = NULL; + goto bail; + } + lt_mem_add_ref((lt_mem_t *)retval, retval->xml, + (lt_destroy_func_t)lt_xml_unref); + + lt_relation_db_parse(retval, &err); + if (lt_error_is_set(err, LT_ERR_ANY)) { + lt_error_print(err, LT_ERR_ANY); + lt_relation_db_unref(retval); + retval = NULL; + lt_error_unref(err); + } + } + bail: + return retval; +} + +/** + * lt_relation_db_ref: + * @relationdb: a #lt_relation_db_t. + * + * Increases the reference count of @relationdb. + * + * Returns: (transfer none): the same @relationddb object. + */ +lt_relation_db_t * +lt_relation_db_ref(lt_relation_db_t *relationdb) +{ + lt_return_val_if_fail (relationdb != NULL, NULL); + + return lt_mem_ref((lt_mem_t *)relationdb); +} + +/** + * lt_relation_db_unref: + * @relationdb: a #lt_relation_db_t. + * + * Decreases the reference count of @relationdb. when its reference count + * drops to 0, the object is finalized (i.e. its memory is freed). + */ +void +lt_relation_db_unref(lt_relation_db_t *relationdb) +{ + if (relationdb) + lt_mem_unref((lt_mem_t *)relationdb); +} + +/** + * lt_relation_db_lookup_lang_from_script: + * + */ +lt_list_t * +lt_relation_db_lookup_lang_from_script(lt_relation_db_t *relationdb, + const lt_script_t *script) +{ + lt_list_t *l, *ll, *retval = NULL; + char *key; + + lt_return_val_if_fail (relationdb != NULL, NULL); + lt_return_val_if_fail (script != NULL, NULL); + + key = strdup(lt_script_get_name(script)); + l = lt_trie_lookup(relationdb->relation_s_l_entries, + lt_strlower(key)); + free(key); + + for (ll = l; ll; ll = lt_list_next(ll)) { + retval = lt_list_append(retval, + lt_lang_ref(lt_list_value(ll)), + (lt_destroy_func_t)lt_lang_unref); + } + + return retval; +} + +/** + * lt_relation_db_lookup_script_from_lang: + * + */ +lt_list_t * +lt_relation_db_lookup_script_from_lang(lt_relation_db_t *relationdb, + const lt_lang_t *lang) +{ + lt_list_t *l, *ll, *retval = NULL; + char *key; + + lt_return_val_if_fail (relationdb != NULL, NULL); + lt_return_val_if_fail (lang != NULL, NULL); + + key = strdup(lt_lang_get_tag(lang)); + l = lt_trie_lookup(relationdb->relation_l_s_entries, + lt_strlower(key)); + free(key); + + for (ll = l; ll; ll = lt_list_next(ll)) { + retval = lt_list_append(retval, + lt_lang_ref(lt_list_value(ll)), + (lt_destroy_func_t)lt_lang_unref); + } + + return retval; +} diff --git a/liblangtag/lt-relation-db.h b/liblangtag/lt-relation-db.h new file mode 100644 index 0000000..d85bf84 --- /dev/null +++ b/liblangtag/lt-relation-db.h @@ -0,0 +1,43 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * lt-relation-db.h + * Copyright (C) 2015 Akira TAGOH + * + * Authors: + * Akira TAGOH <akira@tagoh.org> + * + * You may distribute under the terms of either the GNU + * Lesser General Public License or the Mozilla Public + * License, as specified in the README file. + */ +#if !defined (__LANGTAG_H__INSIDE) && !defined (__LANGTAG_COMPILATION) +#error "Only <liblangtag/langtag.h> can be included directly." +#endif + +#ifndef __LT_RELATION_DB_H__ +#define __LT_RELATION_DB_H__ + +#include <liblangtag/lt-macros.h> + +LT_BEGIN_DECLS + +/** + * lt_relation_db_t: + * + * All the fields in the <structname>lt_relation_db_t</structname> + * structure are private to the #lt_relation_db_t implementation. + */ +typedef struct _lt_relation_db_t lt_relation_db_t; + + +lt_relation_db_t *lt_relation_db_new (void); +lt_relation_db_t *lt_relation_db_ref (lt_relation_db_t *relationdb); +void lt_relation_db_unref (lt_relation_db_t *relationdb); +lt_list_t *lt_relation_db_lookup_lang_from_script(lt_relation_db_t *relationdb, + const lt_script_t *script); +lt_list_t *lt_relation_db_lookup_script_from_lang(lt_relation_db_t *relationdb, + const lt_lang_t *lang); + +LT_END_DECLS + +#endif /* __LT_RELATION_DB_H__ */ diff --git a/liblangtag/lt-xml.c b/liblangtag/lt-xml.c index d337204..0ee34a0 100644 --- a/liblangtag/lt-xml.c +++ b/liblangtag/lt-xml.c @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * lt-xml.c - * Copyright (C) 2011-2012 Akira TAGOH + * Copyright (C) 2011-2015 Akira TAGOH * * Authors: * Akira TAGOH <akira@tagoh.org> @@ -14,6 +14,7 @@ #include "config.h" #endif +#include <stddef.h> #include <sys/stat.h> #include <libxml/parser.h> #include <libxml/xpath.h> @@ -37,6 +38,7 @@ struct _lt_xml_t { xmlDocPtr cldr_bcp47_transform; xmlDocPtr cldr_bcp47_variant; xmlDocPtr cldr_supplemental_likelysubtags; + xmlDocPtr cldr_supplemental_supplementaldata; }; static lt_xml_t *__xml = NULL; @@ -393,6 +395,10 @@ lt_xml_new(void) &__xml->cldr_supplemental_likelysubtags, &err)) goto bail; + if (!lt_xml_read_cldr_supplemental(__xml, "supplementalData.xml", + &__xml->cldr_supplemental_supplementaldata, + &err)) + goto bail; } bail: @@ -434,28 +440,19 @@ xmlDocPtr lt_xml_get_cldr(lt_xml_t *xml, lt_xml_cldr_t type) { + xmlDocPtr *pref; + int idx; + + LT_ASSERT_STATIC ((sizeof (lt_xml_t) - offsetof (lt_xml_t, cldr_bcp47_calendar)) == (sizeof (lt_pointer_t) * ((LT_XML_CLDR_BCP47_END - LT_XML_CLDR_BCP47_BEGIN + 1) + (LT_XML_CLDR_SUPPLEMENTAL_END - LT_XML_CLDR_SUPPLEMENTAL_BEGIN + 1)))); + lt_return_val_if_fail (xml != NULL, NULL); + lt_return_val_if_fail (type > LT_XML_CLDR_BEGIN && type < LT_XML_CLDR_END, NULL); - switch (type) { - case LT_XML_CLDR_BCP47_CALENDAR: - return xml->cldr_bcp47_calendar; - case LT_XML_CLDR_BCP47_COLLATION: - return xml->cldr_bcp47_collation; - case LT_XML_CLDR_BCP47_CURRENCY: - return xml->cldr_bcp47_currency; - case LT_XML_CLDR_BCP47_NUMBER: - return xml->cldr_bcp47_number; - case LT_XML_CLDR_BCP47_TIMEZONE: - return xml->cldr_bcp47_timezone; - case LT_XML_CLDR_BCP47_TRANSFORM: - return xml->cldr_bcp47_transform; - case LT_XML_CLDR_BCP47_VARIANT: - return xml->cldr_bcp47_variant; - case LT_XML_CLDR_SUPPLEMENTAL_LIKELY_SUBTAGS: - return xml->cldr_supplemental_likelysubtags; - default: - break; - } + pref = &xml->cldr_bcp47_calendar; + if (type >= LT_XML_CLDR_DUMMY1) + idx = type - LT_XML_CLDR_DUMMY1 + LT_XML_CLDR_BCP47_END; + else + idx = type; - return NULL; + return pref[idx - 1]; } diff --git a/liblangtag/lt-xml.h b/liblangtag/lt-xml.h index 856d6c6..917806a 100644 --- a/liblangtag/lt-xml.h +++ b/liblangtag/lt-xml.h @@ -1,7 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * lt-xml.h - * Copyright (C) 2011-2012 Akira TAGOH + * Copyright (C) 2011-2015 Akira TAGOH * * Authors: * Akira TAGOH <akira@tagoh.org> @@ -32,8 +32,9 @@ typedef enum _lt_xml_cldr_t { LT_XML_CLDR_BCP47_END = LT_XML_CLDR_BCP47_VARIANT, LT_XML_CLDR_DUMMY1 = 100, LT_XML_CLDR_SUPPLEMENTAL_LIKELY_SUBTAGS, + LT_XML_CLDR_SUPPLEMENTAL_SUPPLEMENTAL_DATA, LT_XML_CLDR_SUPPLEMENTAL_BEGIN = LT_XML_CLDR_SUPPLEMENTAL_LIKELY_SUBTAGS, - LT_XML_CLDR_SUPPLEMENTAL_END = LT_XML_CLDR_SUPPLEMENTAL_LIKELY_SUBTAGS, + LT_XML_CLDR_SUPPLEMENTAL_END = LT_XML_CLDR_SUPPLEMENTAL_SUPPLEMENTAL_DATA, LT_XML_CLDR_END } lt_xml_cldr_t; diff --git a/tests/tag.c b/tests/tag.c index aa8c109..30fba88 100644 --- a/tests/tag.c +++ b/tests/tag.c @@ -34,7 +34,7 @@ main(int argc, if (lt_strcmp0(argv[1], "help") == 0) { help: printf("Usage: %s <command> ...\n" - "commands: canonicalize, ecanonicalize, dump, from_locale, from_locale_s, lookup, match, to_locale, transform\n", + "commands: canonicalize, ecanonicalize, dump, from_locale, from_locale_s, lookup, match, script, to_locale, transform\n", argv[0]); } else if (lt_strcmp0(argv[1], "canonicalize") == 0) { char *s; @@ -105,6 +105,20 @@ main(int argc, printf("%s -> %s\n", argv[2], r); free(r); } + } else if (lt_strcmp0(argv[1], "script") == 0) { + if (lt_tag_parse(tag, argv[2], NULL)) { + lt_relation_db_t *db = lt_db_get_relation(); + const lt_lang_t *lang = lt_tag_get_language(tag); + lt_list_t *l, *ll = lt_relation_db_lookup_script_from_lang(db, lang); + + printf("%s: ", lt_lang_get_tag(lang)); + for (l = ll; l; l = lt_list_next(l)) { + lt_script_t *script = lt_list_value(l); + + printf("%s ", lt_script_get_tag(script)); + } + printf("\n"); + } } else { goto help; } |