LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/langtag/liblangtag - lt-tag.c (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 297 1013 29.3 %
Date: 2012-12-17 Functions: 32 61 52.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
       2             : /* 
       3             :  * lt-tag.c
       4             :  * Copyright (C) 2011-2012 Akira TAGOH
       5             :  * 
       6             :  * Authors:
       7             :  *   Akira TAGOH  <akira@tagoh.org>
       8             :  * 
       9             :  * You may distribute under the terms of either the GNU
      10             :  * Lesser General Public License or the Mozilla Public
      11             :  * License, as specified in the README file.
      12             :  */
      13             : #ifdef HAVE_CONFIG_H
      14             : #include "config.h"
      15             : #endif
      16             : 
      17             : #include <ctype.h>
      18             : #ifndef _WIN32
      19             : #include <langinfo.h>
      20             : #endif
      21             : #include <locale.h>
      22             : #ifndef HAVE_STDINT_H
      23             : typedef int int32_t;
      24             : #else
      25             : #include <stdint.h>
      26             : #endif
      27             : #include <string.h>
      28             : #include <libxml/xpath.h>
      29             : #include "lt-database.h"
      30             : #include "lt-error.h"
      31             : #include "lt-ext-module-private.h"
      32             : #include "lt-extension-private.h"
      33             : #include "lt-localealias.h"
      34             : #include "lt-mem.h"
      35             : #include "lt-messages.h"
      36             : #include "lt-string.h"
      37             : #include "lt-utils.h"
      38             : #include "lt-xml.h"
      39             : #include "lt-tag.h"
      40             : #include "lt-tag-private.h"
      41             : 
      42             : 
      43             : /**
      44             :  * SECTION: lt-tag
      45             :  * @Short_Description: A container class for Language tag
      46             :  * @Title: Container - Tag
      47             :  *
      48             :  * This container class provides an interface to deal with the language tag.
      49             :  */
      50             : typedef struct _lt_tag_scanner_t {
      51             :         lt_mem_t  parent;
      52             :         char     *string;
      53             :         size_t    length;
      54             :         size_t    position;
      55             : } lt_tag_scanner_t;
      56             : 
      57             : struct _lt_tag_t {
      58             :         lt_mem_t            parent;
      59             :         int32_t             wildcard_map;
      60             :         lt_tag_state_t      state;
      61             :         lt_string_t        *tag_string;
      62             :         lt_lang_t          *language;
      63             :         lt_extlang_t       *extlang;
      64             :         lt_script_t        *script;
      65             :         lt_region_t        *region;
      66             :         lt_list_t          *variants;
      67             :         lt_extension_t     *extension;
      68             :         lt_string_t        *privateuse;
      69             :         lt_grandfathered_t *grandfathered;
      70             : };
      71             : 
      72             : /*< private >*/
      73             : static lt_bool_t
      74           0 : _lt_tag_string_compare(const lt_string_t *v1,
      75             :                        const lt_string_t *v2)
      76             : {
      77           0 :         lt_bool_t retval = FALSE;
      78             :         char *s1, *s2;
      79             : 
      80           0 :         if (v1 == v2)
      81           0 :                 return TRUE;
      82             : 
      83           0 :         s1 = v1 ? lt_strlower(strdup(lt_string_value(v1))) : NULL;
      84           0 :         s2 = v2 ? lt_strlower(strdup(lt_string_value(v2))) : NULL;
      85             : 
      86           0 :         if (lt_strcmp0(s1, "*") == 0 ||
      87           0 :             lt_strcmp0(s2, "*") == 0) {
      88           0 :                 retval = TRUE;
      89           0 :                 goto bail;
      90             :         }
      91             : 
      92           0 :         retval = lt_strcmp0(s1, s2) == 0;
      93             :   bail:
      94           0 :         free(s1);
      95           0 :         free(s2);
      96             : 
      97           0 :         return retval;
      98             : }
      99             : 
     100             : static lt_tag_scanner_t *
     101          22 : lt_tag_scanner_new(const char *tag)
     102             : {
     103          22 :         lt_tag_scanner_t *retval = lt_mem_alloc_object(sizeof (lt_tag_scanner_t));
     104             : 
     105          22 :         if (retval) {
     106          22 :                 retval->string = strdup(tag);
     107          22 :                 lt_mem_add_ref(&retval->parent, retval->string, free);
     108          22 :                 retval->length = strlen(tag);
     109             :         }
     110             : 
     111          22 :         return retval;
     112             : }
     113             : 
     114             : static void
     115          24 : lt_tag_scanner_unref(lt_tag_scanner_t *scanner)
     116             : {
     117          24 :         if (scanner)
     118          22 :                 lt_mem_unref(&scanner->parent);
     119          24 : }
     120             : 
     121             : static lt_bool_t
     122          54 : lt_tag_scanner_get_token(lt_tag_scanner_t  *scanner,
     123             :                          char             **retval,
     124             :                          size_t            *length,
     125             :                          lt_error_t       **error)
     126             : {
     127          54 :         lt_string_t *string = NULL;
     128             :         char c;
     129          54 :         lt_error_t *err = NULL;
     130             : 
     131          54 :         lt_return_val_if_fail (scanner != NULL, FALSE);
     132             : 
     133          54 :         if (scanner->position >= scanner->length) {
     134           0 :                 lt_error_set(&err, LT_ERR_EOT,
     135             :                              "No more tokens in buffer");
     136           0 :                 goto bail;
     137             :         }
     138             : 
     139          54 :         string = lt_string_new(NULL);
     140         168 :         while (scanner->position < scanner->length) {
     141         114 :                 c = scanner->string[scanner->position++];
     142         114 :                 if (c == 0) {
     143           0 :                         if (lt_string_length(string) == 0) {
     144           0 :                                 lt_error_set(&err, LT_ERR_EOT,
     145             :                                              "No more tokens in buffer");
     146             :                         }
     147           0 :                         scanner->position--;
     148           0 :                         break;
     149             :                 }
     150         114 :                 if (c == '*') {
     151           0 :                         if (lt_string_length(string) > 0) {
     152           0 :                                 lt_error_set(&err, LT_ERR_FAIL_ON_SCANNER,
     153             :                                              "Invalid wildcard: positon = %zd",
     154           0 :                                              scanner->position - 1);
     155           0 :                                 break;
     156             :                         }
     157         114 :                 } else if (!isalnum(c) && c != '-' && c != 0) {
     158           0 :                         lt_error_set(&err, LT_ERR_FAIL_ON_SCANNER,
     159             :                                      "Invalid character for tag: '%c'", c);
     160           0 :                         break;
     161             :                 }
     162         114 :                 lt_string_append_c(string, c);
     163             : 
     164         114 :                 if (c == '-' ||
     165             :                     c == '*')
     166             :                         break;
     167         160 :                 if (scanner->string[scanner->position] == '-' ||
     168          68 :                     scanner->string[scanner->position] == 0)
     169             :                         break;
     170             :         }
     171             :   bail:
     172          54 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
     173           0 :                 if (error)
     174           0 :                         *error = lt_error_ref(err);
     175             :                 else
     176           0 :                         lt_error_print(err, LT_ERR_ANY);
     177           0 :                 lt_error_unref(err);
     178           0 :                 lt_string_unref(string);
     179           0 :                 *retval = NULL;
     180           0 :                 *length = 0;
     181             : 
     182           0 :                 return FALSE;
     183             :         }
     184             : 
     185          54 :         *length = lt_string_length(string);
     186          54 :         *retval = lt_string_free(string, FALSE);
     187             : 
     188          54 :         return TRUE;
     189             : }
     190             : 
     191             : static lt_bool_t
     192          74 : lt_tag_scanner_is_eof(lt_tag_scanner_t *scanner)
     193             : {
     194          74 :         lt_return_val_if_fail (scanner != NULL, TRUE);
     195          74 :         lt_return_val_if_fail (scanner->position <= scanner->length, TRUE);
     196             : 
     197         128 :         return scanner->string[scanner->position] == 0 ||
     198          54 :                 scanner->position >= scanner->length;
     199             : }
     200             : 
     201             : static int
     202           0 : _lt_tag_variant_compare(const lt_pointer_t a,
     203             :                         const lt_pointer_t b)
     204             : {
     205           0 :         return (unsigned long)a - (unsigned long)b;
     206             : }
     207             : 
     208             : #define DEFUNC_TAG_FREE(__func__)                                       \
     209             :         LT_INLINE_FUNC void                                             \
     210             :         lt_tag_free_ ##__func__ (lt_tag_t *tag)                         \
     211             :         {                                                               \
     212             :                 if (tag->__func__) {                                 \
     213             :                         lt_mem_delete_ref(&tag->parent, tag->__func__);       \
     214             :                         tag->__func__ = NULL;                                \
     215             :                 }                                                       \
     216             :         }
     217             : 
     218          30 : DEFUNC_TAG_FREE (language)
     219          24 : DEFUNC_TAG_FREE (extlang)
     220          34 : DEFUNC_TAG_FREE (script)
     221          38 : DEFUNC_TAG_FREE (region)
     222          24 : DEFUNC_TAG_FREE (variants)
     223          24 : DEFUNC_TAG_FREE (extension)
     224          48 : DEFUNC_TAG_FREE (grandfathered)
     225          32 : DEFUNC_TAG_FREE (tag_string)
     226             : 
     227             : #undef DEFUNC_TAG_FREE
     228             : 
     229             : #define DEFUNC_TAG_SET(__func__, __unref_func__)                        \
     230             :         LT_INLINE_FUNC void                                             \
     231             :         lt_tag_set_ ##__func__ (lt_tag_t *tag, lt_pointer_t p)          \
     232             :         {                                                               \
     233             :                 lt_tag_free_ ##__func__ (tag);                          \
     234             :                 if (p) {                                                \
     235             :                         tag->__func__ = p;                           \
     236             :                         lt_mem_add_ref(&tag->parent, tag->__func__,   \
     237             :                                        (lt_destroy_func_t)__unref_func__); \
     238             :                 }                                                       \
     239             :         }
     240             : 
     241           4 : DEFUNC_TAG_SET (language, lt_lang_unref)
     242           0 : DEFUNC_TAG_SET (extlang, lt_extlang_unref)
     243           8 : DEFUNC_TAG_SET (script, lt_script_unref)
     244          10 : DEFUNC_TAG_SET (region, lt_region_unref)
     245           0 : DEFUNC_TAG_SET (extension, lt_extension_unref)
     246          24 : DEFUNC_TAG_SET (grandfathered, lt_grandfathered_unref)
     247             : 
     248             : LT_INLINE_FUNC void
     249           0 : lt_tag_set_variant(lt_tag_t     *tag,
     250             :                    lt_pointer_t  p)
     251             : {
     252           0 :         lt_bool_t no_variants = (tag->variants == NULL);
     253             : 
     254           0 :         if (p) {
     255           0 :                 tag->variants = lt_list_append(tag->variants, p, (lt_destroy_func_t)lt_variant_unref);
     256           0 :                 if (no_variants)
     257           0 :                         lt_mem_add_ref(&tag->parent, tag->variants, lt_list_free);
     258             :         } else {
     259           0 :                 lt_warn_if_reached();
     260             :         }
     261           0 : }
     262             : 
     263             : #undef DEFUNC_TAG_SET
     264             : 
     265             : LT_INLINE_FUNC void
     266          46 : lt_tag_add_tag_string(lt_tag_t   *tag,
     267             :                       const char *s)
     268             : {
     269          46 :         if (!tag->tag_string) {
     270          34 :                 tag->tag_string = lt_string_new(NULL);
     271          34 :                 lt_mem_add_ref(&tag->parent, tag->tag_string,
     272             :                                (lt_destroy_func_t)lt_string_unref);
     273             :         }
     274          46 :         if (s) {
     275          46 :                 if (lt_string_length(tag->tag_string) > 0)
     276          12 :                         lt_string_append_c(tag->tag_string, '-');
     277          46 :                 lt_string_append(tag->tag_string, s);
     278             :         } else {
     279           0 :                 lt_warn_if_reached();
     280             :         }
     281          46 : }
     282             : 
     283             : static const char *
     284           0 : lt_tag_get_locale_from_locale_alias(const char *alias)
     285             : {
     286             :         int i;
     287             : 
     288           0 :         lt_return_val_if_fail (alias != NULL, NULL);
     289             : 
     290           0 :         for (i = 0; __lt_localealias_tables[i].alias != NULL; i++) {
     291           0 :                 if (lt_strcasecmp(alias, __lt_localealias_tables[i].alias) == 0)
     292           0 :                         return __lt_localealias_tables[i].locale;
     293             :         }
     294             : 
     295           0 :         return NULL;
     296             : }
     297             : 
     298             : static void
     299           0 : lt_tag_fill_wildcard(lt_tag_t       *tag,
     300             :                      lt_tag_state_t  begin,
     301             :                      lt_tag_state_t  end)
     302             : {
     303             :         lt_tag_state_t i;
     304             :         lt_lang_db_t *langdb;
     305             :         lt_extlang_db_t *extlangdb;
     306             :         lt_script_db_t *scriptdb;
     307             :         lt_region_db_t *regiondb;
     308             :         lt_variant_db_t *variantdb;
     309             :         lt_extension_t *e;
     310             : 
     311           0 :         for (i = begin; i < end; i++) {
     312           0 :                 tag->wildcard_map |= (1 << (i - 1));
     313           0 :                 switch (i) {
     314             :                     case STATE_LANG:
     315           0 :                             langdb = lt_db_get_lang();
     316           0 :                             lt_tag_set_language(tag, lt_lang_db_lookup(langdb, "*"));
     317           0 :                             lt_lang_db_unref(langdb);
     318           0 :                             break;
     319             :                     case STATE_EXTLANG:
     320           0 :                             extlangdb = lt_db_get_extlang();
     321           0 :                             lt_tag_set_extlang(tag, lt_extlang_db_lookup(extlangdb, "*"));
     322           0 :                             lt_extlang_db_unref(extlangdb);
     323           0 :                             break;
     324             :                     case STATE_SCRIPT:
     325           0 :                             scriptdb = lt_db_get_script();
     326           0 :                             lt_tag_set_script(tag, lt_script_db_lookup(scriptdb, "*"));
     327           0 :                             lt_script_db_unref(scriptdb);
     328           0 :                             break;
     329             :                     case STATE_REGION:
     330           0 :                             regiondb = lt_db_get_region();
     331           0 :                             lt_tag_set_region(tag, lt_region_db_lookup(regiondb, "*"));
     332           0 :                             lt_region_db_unref(regiondb);
     333           0 :                             break;
     334             :                     case STATE_VARIANT:
     335           0 :                             variantdb = lt_db_get_variant();
     336           0 :                             lt_tag_set_variant(tag, lt_variant_db_lookup(variantdb, "*"));
     337           0 :                             lt_variant_db_unref(variantdb);
     338           0 :                             break;
     339             :                     case STATE_EXTENSION:
     340           0 :                             e = lt_extension_create();
     341           0 :                             lt_extension_add_singleton(e, '*', NULL, NULL);
     342           0 :                             lt_tag_set_extension(tag, e);
     343           0 :                             break;
     344             :                     case STATE_PRIVATEUSE:
     345           0 :                             lt_string_clear(tag->privateuse);
     346           0 :                             lt_string_append(tag->privateuse, "*");
     347           0 :                             break;
     348             :                     default:
     349           0 :                             break;
     350             :                 }
     351             :         }
     352           0 : }
     353             : 
     354             : static void
     355          24 : lt_tag_parser_init(lt_tag_t *tag)
     356             : {
     357          24 :         lt_tag_clear(tag);
     358          24 :         tag->state = STATE_NONE;
     359          24 : }
     360             : 
     361             : static lt_bool_t
     362          54 : lt_tag_parse_prestate(lt_tag_t    *tag,
     363             :                       const char  *token,
     364             :                       size_t       length,
     365             :                       lt_error_t **error)
     366             : {
     367          54 :         lt_bool_t retval = TRUE;
     368             : 
     369          54 :         if (lt_strcmp0(token, "-") == 0) {
     370          22 :                 switch (tag->state) {
     371             :                     case STATE_PRE_EXTLANG:
     372          18 :                             tag->state = STATE_EXTLANG;
     373          18 :                             break;
     374             :                     case STATE_PRE_SCRIPT:
     375           0 :                             tag->state = STATE_SCRIPT;
     376           0 :                             break;
     377             :                     case STATE_PRE_REGION:
     378           4 :                             tag->state = STATE_REGION;
     379           4 :                             break;
     380             :                     case STATE_PRE_VARIANT:
     381           0 :                             tag->state = STATE_VARIANT;
     382           0 :                             break;
     383             :                     case STATE_PRE_EXTENSION:
     384           0 :                             tag->state = STATE_EXTENSION;
     385           0 :                             break;
     386             :                     case STATE_IN_EXTENSION:
     387           0 :                             tag->state = STATE_EXTENSIONTOKEN;
     388           0 :                             break;
     389             :                     case STATE_IN_EXTENSIONTOKEN:
     390           0 :                             tag->state = STATE_EXTENSIONTOKEN2;
     391           0 :                             break;
     392             :                     case STATE_PRE_PRIVATEUSE:
     393           0 :                             tag->state = STATE_PRIVATEUSE;
     394           0 :                             break;
     395             :                     case STATE_IN_PRIVATEUSE:
     396           0 :                             tag->state = STATE_PRIVATEUSETOKEN;
     397           0 :                             break;
     398             :                     case STATE_IN_PRIVATEUSETOKEN:
     399           0 :                             tag->state = STATE_PRIVATEUSETOKEN2;
     400           0 :                             break;
     401             :                     default:
     402           0 :                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     403             :                                          "Invalid syntax found during parsing a token: %s",
     404             :                                          token);
     405           0 :                             retval = FALSE;
     406           0 :                             break;
     407             :                 }
     408             :         } else {
     409          32 :                 retval = FALSE;
     410             :         }
     411             : 
     412          54 :         return retval;
     413             : }
     414             : 
     415             : static lt_bool_t
     416          32 : lt_tag_parse_state(lt_tag_t    *tag,
     417             :                    const char  *token,
     418             :                    size_t       length,
     419             :                    lt_error_t **error)
     420             : {
     421          32 :         lt_bool_t retval = TRUE;
     422             :         const char *p;
     423             : 
     424          32 :         switch (tag->state) {
     425             :             case STATE_LANG:
     426          22 :                     if (length == 1) {
     427           0 :                             if (lt_strcasecmp(token, "x") == 0) {
     428           0 :                                     lt_string_append(tag->privateuse, token);
     429           0 :                                     tag->state = STATE_IN_PRIVATEUSE;
     430           0 :                                     break;
     431             :                             } else {
     432             :                               invalid_tag:
     433           0 :                                     lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     434             :                                                  "Invalid language subtag: %s", token);
     435           0 :                                     break;
     436             :                             }
     437          42 :                     } else if (length >= 2 && length <= 3) {
     438          20 :                             lt_lang_db_t *langdb = lt_db_get_lang();
     439             : 
     440             :                             /* shortest ISO 639 code */
     441          20 :                             tag->language = lt_lang_db_lookup(langdb, token);
     442          20 :                             lt_lang_db_unref(langdb);
     443          20 :                             if (!tag->language) {
     444           0 :                                     lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     445             :                                                  "Unknown ISO 639 code: %s",
     446             :                                                  token);
     447           0 :                                     break;
     448             :                             }
     449             :                             /* validate if it's really shortest one */
     450          20 :                             p = lt_lang_get_tag(tag->language);
     451          20 :                             if (!p || lt_strcasecmp(token, p) != 0) {
     452           0 :                                     lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     453             :                                                  "No such language subtag: %s",
     454             :                                                  token);
     455           0 :                                     lt_lang_unref(tag->language);
     456           0 :                                     tag->language = NULL;
     457           0 :                                     break;
     458             :                             }
     459          20 :                             lt_mem_add_ref(&tag->parent, tag->language,
     460             :                                            (lt_destroy_func_t)lt_lang_unref);
     461          20 :                             tag->state = STATE_PRE_EXTLANG;
     462           2 :                     } else if (length == 4) {
     463             :                             /* reserved for future use */
     464           0 :                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     465             :                                          "Reserved for future use: %s",
     466             :                                          token);
     467           2 :                     } else if (length >= 5 && length <= 8) {
     468             :                             /* registered language subtag */
     469           2 :                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     470             :                                          "XXX: registered language tag: %s",
     471             :                                          token);
     472             :                     } else {
     473             :                             goto invalid_tag;
     474             :                     }
     475          22 :                     break;
     476             :             case STATE_EXTLANG:
     477           6 :                     if (length == 3) {
     478           0 :                             lt_extlang_db_t *extlangdb = lt_db_get_extlang();
     479             : 
     480           0 :                             tag->extlang = lt_extlang_db_lookup(extlangdb, token);
     481           0 :                             lt_extlang_db_unref(extlangdb);
     482           0 :                             if (tag->extlang) {
     483           0 :                                     const char *prefix = lt_extlang_get_prefix(tag->extlang);
     484           0 :                                     const char *subtag = lt_extlang_get_tag(tag->extlang);
     485           0 :                                     const char *lang = lt_lang_get_better_tag(tag->language);
     486             : 
     487           0 :                                     if (prefix &&
     488           0 :                                         lt_strcasecmp(prefix, lang) != 0) {
     489           0 :                                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     490             :                                                          "extlang '%s' is supposed to be used with %s, but %s",
     491             :                                                          subtag, prefix, lang);
     492           0 :                                             lt_extlang_unref(tag->extlang);
     493           0 :                                             tag->extlang = NULL;
     494             :                                     } else {
     495           0 :                                             lt_mem_add_ref(&tag->parent, tag->extlang,
     496             :                                                            (lt_destroy_func_t)lt_extlang_unref);
     497           0 :                                             tag->state = STATE_PRE_SCRIPT;
     498             :                                     }
     499           0 :                                     break;
     500             :                             }
     501             :                             /* try to check something else */
     502             :                     } else {
     503             :                             /* it may be a script */
     504             :                     }
     505             :             case STATE_SCRIPT:
     506           6 :                     if (length == 4) {
     507           4 :                             lt_script_db_t *scriptdb = lt_db_get_script();
     508             : 
     509           4 :                             lt_tag_set_script(tag, lt_script_db_lookup(scriptdb, token));
     510           4 :                             lt_script_db_unref(scriptdb);
     511           4 :                             if (tag->script) {
     512           4 :                                     tag->state = STATE_PRE_REGION;
     513           4 :                                     break;
     514             :                             }
     515             :                             /* try to check something else */
     516             :                     } else {
     517             :                             /* it may be a region */
     518             :                     }
     519             :             case STATE_REGION:
     520           6 :                     if (length == 2 ||
     521           0 :                         (length == 3 &&
     522           0 :                          isdigit(token[0]) &&
     523           0 :                          isdigit(token[1]) &&
     524           0 :                          isdigit(token[2]))) {
     525           6 :                             lt_region_db_t *regiondb = lt_db_get_region();
     526             : 
     527           6 :                             lt_tag_set_region(tag, lt_region_db_lookup(regiondb, token));
     528           6 :                             lt_region_db_unref(regiondb);
     529           6 :                             if (tag->region) {
     530           6 :                                     tag->state = STATE_PRE_VARIANT;
     531           6 :                                     break;
     532             :                             }
     533             :                             /* try to check something else */
     534             :                     } else {
     535             :                             /* it may be a variant */
     536             :                     }
     537             :             case STATE_VARIANT:
     538           0 :                     if ((length >=5 && length <= 8) ||
     539           0 :                         (length == 4 && isdigit(token[0]))) {
     540           0 :                             lt_variant_db_t *variantdb = lt_db_get_variant();
     541             :                             lt_variant_t *variant;
     542             : 
     543           0 :                             variant = lt_variant_db_lookup(variantdb, token);
     544           0 :                             lt_variant_db_unref(variantdb);
     545           0 :                             if (variant) {
     546           0 :                                     const lt_list_t *prefixes = lt_variant_get_prefix(variant), *l;
     547           0 :                                     char *langtag = lt_tag_canonicalize(tag, error);
     548           0 :                                     lt_string_t *str_prefixes = lt_string_new(NULL);
     549           0 :                                     lt_bool_t matched = FALSE;
     550             : 
     551           0 :                                     if (error && lt_error_is_set(*error, LT_ERR_ANY)) {
     552             :                                             /* ignore it and fallback to the original tag string */
     553           0 :                                             lt_error_clear(*error);
     554           0 :                                             *error = NULL;
     555           0 :                                             langtag = strdup(lt_string_value(tag->tag_string));
     556             :                                     }
     557           0 :                                     for (l = prefixes; l != NULL; l = lt_list_next(l)) {
     558           0 :                                             const char *s = lt_list_value(l);
     559             : 
     560           0 :                                             if (lt_string_length(str_prefixes) > 0)
     561           0 :                                                     lt_string_append(str_prefixes, ",");
     562           0 :                                             lt_string_append(str_prefixes, s);
     563             : 
     564           0 :                                             if (lt_strncasecmp(s, langtag, strlen(s)) == 0) {
     565           0 :                                                     matched = TRUE;
     566           0 :                                                     break;
     567             :                                             }
     568             :                                     }
     569           0 :                                     if (prefixes && !matched) {
     570           0 :                                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     571             :                                                          "variant '%s' is supposed to be used with %s, but %s",
     572             :                                                          token, lt_string_value(str_prefixes), langtag);
     573           0 :                                             lt_variant_unref(variant);
     574             :                                     } else {
     575           0 :                                             if (!tag->variants) {
     576           0 :                                                     lt_tag_set_variant(tag, variant);
     577             :                                             } else {
     578           0 :                                                     lt_list_t *prefixes = (lt_list_t *)lt_variant_get_prefix(variant);
     579             :                                                     const char *tstr;
     580             : 
     581           0 :                                                     lt_tag_free_tag_string(tag);
     582           0 :                                                     tstr = lt_tag_get_string(tag);
     583           0 :                                                     if (prefixes && !lt_list_find_custom(prefixes, (const lt_pointer_t)tstr, (lt_compare_func_t)lt_strcmp0)) {
     584           0 :                                                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     585             :                                                                          "Variant isn't allowed for %s: %s",
     586             :                                                                          tstr,
     587             :                                                                          lt_variant_get_tag(variant));
     588           0 :                                                             lt_variant_unref(variant);
     589           0 :                                                     } else if (!prefixes && lt_list_find_custom(tag->variants, variant, _lt_tag_variant_compare)) {
     590           0 :                                                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     591             :                                                                          "Duplicate variants: %s",
     592             :                                                                          lt_variant_get_tag(variant));
     593           0 :                                                             lt_variant_unref(variant);
     594             :                                                     } else {
     595           0 :                                                             tag->variants = lt_list_append(tag->variants,
     596             :                                                                                            variant,
     597             :                                                                                            (lt_destroy_func_t)lt_variant_unref);
     598             :                                                     }
     599             :                                             }
     600             :                                             /* multiple variants are allowed. */
     601           0 :                                             tag->state = STATE_PRE_VARIANT;
     602             :                                     }
     603           0 :                                     if (langtag)
     604           0 :                                             free(langtag);
     605           0 :                                     lt_string_unref(str_prefixes);
     606           0 :                                     break;
     607             :                             }
     608             :                             /* try to check something else */
     609             :                     } else {
     610             :                             /* it may be an extension */
     611             :                     }
     612             :             case STATE_EXTENSION:
     613             :             extension:
     614           0 :                     if (length == 1 &&
     615           0 :                         token[0] != 'x' &&
     616           0 :                         token[0] != 'X' &&
     617           0 :                         token[0] != '*' &&
     618           0 :                         token[0] != '-') {
     619           0 :                             if (!tag->extension)
     620           0 :                                     lt_tag_set_extension(tag, lt_extension_create());
     621           0 :                             if (lt_extension_has_singleton(tag->extension, token[0])) {
     622           0 :                                     lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     623             :                                                  "Duplicate singleton for extension: %s", token);
     624             :                             } else {
     625           0 :                                     if (lt_extension_add_singleton(tag->extension,
     626           0 :                                                                     token[0],
     627             :                                                                     tag, error)) {
     628           0 :                                             tag->state = STATE_IN_EXTENSION;
     629             :                                     }
     630             :                             }
     631           0 :                             break;
     632             :                     } else {
     633             :                             /* it may be a private use */
     634             :                     }
     635             :             case STATE_PRIVATEUSE:
     636           0 :                     if (length == 1 && (token[0] == 'x' || token[0] == 'X')) {
     637           0 :                             lt_string_append(tag->privateuse, token);
     638           0 :                             tag->state = STATE_IN_PRIVATEUSE;
     639             :                     } else {
     640             :                             /* No state to try */
     641           0 :                             retval = FALSE;
     642             :                     }
     643           0 :                     break;
     644             :             case STATE_EXTENSIONTOKEN:
     645             :             case STATE_EXTENSIONTOKEN2:
     646           0 :                     if (length >= 2 && length <= 8) {
     647           0 :                             if (lt_extension_add_tag(tag->extension,
     648             :                                                       token, error))
     649           0 :                                     tag->state = STATE_IN_EXTENSIONTOKEN;
     650             :                     } else {
     651           0 :                             if (tag->state == STATE_EXTENSIONTOKEN2 &&
     652           0 :                                 lt_extension_validate_state(tag->extension)) {
     653             :                                     /* No need to destroy the previous tokens.
     654             :                                      * fallback to check the extension again.
     655             :                                      */
     656           0 :                                     goto extension;
     657             :                             }
     658           0 :                             lt_extension_cancel_tag(tag->extension);
     659             :                             /* No state to try */
     660           0 :                             retval = FALSE;
     661             :                     }
     662           0 :                     break;
     663             :             case STATE_PRIVATEUSETOKEN:
     664             :             case STATE_PRIVATEUSETOKEN2:
     665           0 :                     if (length <= 8) {
     666           0 :                             lt_string_append_printf(tag->privateuse, "-%s", token);
     667           0 :                             tag->state = STATE_IN_PRIVATEUSETOKEN;
     668             :                     } else {
     669             :                             /* 'x'/'X' is reserved singleton for the private use subtag.
     670             :                              * so nothing to fallback to anything else.
     671             :                              */
     672           0 :                             lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     673             :                                          "Invalid tag for the private use: token = '%s'",
     674             :                                          token);
     675             :                     }
     676           0 :                     break;
     677             :             default:
     678           0 :                     lt_error_set(error, LT_ERR_FAIL_ON_SCANNER,
     679             :                                  "Unable to parse tag: %s, token = '%s' state = %d",
     680           0 :                                  lt_string_value(tag->tag_string), token, tag->state);
     681           0 :                     break;
     682             :         }
     683          32 :         if (lt_error_is_set(*error, LT_ERR_ANY))
     684           2 :                 retval = FALSE;
     685             : 
     686          32 :         return retval;
     687             : }
     688             : 
     689             : static lt_bool_t
     690          24 : _lt_tag_parse(lt_tag_t    *tag,
     691             :               const char  *langtag,
     692             :               lt_bool_t    allow_wildcard,
     693             :               lt_error_t **error)
     694             : {
     695          24 :         lt_tag_scanner_t *scanner = NULL;
     696             :         lt_grandfathered_db_t *grandfathereddb;
     697          24 :         char *token = NULL;
     698          24 :         size_t len = 0;
     699          24 :         lt_error_t *err = NULL;
     700          24 :         lt_bool_t retval = TRUE;
     701          24 :         lt_tag_state_t wildcard = STATE_NONE;
     702          24 :         int count = 0;
     703             : 
     704          24 :         lt_return_val_if_fail (tag != NULL, FALSE);
     705          24 :         lt_return_val_if_fail (langtag != NULL, FALSE);
     706             : 
     707          24 :         if (tag->state == STATE_NONE) {
     708          24 :                 grandfathereddb = lt_db_get_grandfathered();
     709          24 :                 lt_tag_set_grandfathered(tag, lt_grandfathered_db_lookup(grandfathereddb, langtag));
     710          24 :                 lt_grandfathered_db_unref(grandfathereddb);
     711          24 :                 if (tag->grandfathered) {
     712             :                         /* no need to lookup anymore. */
     713           2 :                         goto bail;
     714             :                 }
     715          22 :                 tag->state = STATE_LANG;
     716             :         } else {
     717           0 :                 if (tag->state == STATE_PRE_EXTLANG ||
     718           0 :                     tag->state == STATE_PRE_SCRIPT ||
     719           0 :                     tag->state == STATE_PRE_REGION ||
     720           0 :                     tag->state == STATE_PRE_VARIANT ||
     721           0 :                     tag->state == STATE_PRE_EXTENSION ||
     722           0 :                     tag->state == STATE_PRE_PRIVATEUSE)
     723           0 :                         tag->state++;
     724             :         }
     725             : 
     726          22 :         scanner = lt_tag_scanner_new(langtag);
     727          96 :         while (!lt_tag_scanner_is_eof(scanner)) {
     728          54 :                 if (token) {
     729          32 :                         free(token);
     730          32 :                         token = NULL;
     731             :                 }
     732          54 :                 if (!lt_tag_scanner_get_token(scanner, &token, &len, &err)) {
     733           0 :                         if (err)
     734           0 :                                 break;
     735           0 :                         lt_error_set(&err, LT_ERR_FAIL_ON_SCANNER,
     736             :                                      "Unrecoverable error");
     737           0 :                         break;
     738             :                 }
     739          54 :                 count++;
     740          54 :                 if (!token || len == 0) {
     741           0 :                         lt_error_set(&err, LT_ERR_FAIL_ON_SCANNER,
     742             :                                      "No valid tokens found");
     743           0 :                         break;
     744             :                 }
     745          54 :                 if (!lt_tag_parse_prestate(tag, token, len, &err)) {
     746          32 :                         if (err)
     747           0 :                                 break;
     748          32 :                         if (allow_wildcard && lt_strcmp0(token, "*") == 0) {
     749           0 :                                 wildcard = tag->state;
     750           0 :                                 if (tag->state == STATE_LANG)
     751           0 :                                         tag->state += 1;
     752             :                                 else
     753           0 :                                         tag->state -= 1;
     754             :                         } else {
     755          32 :                                 if (!lt_tag_parse_state(tag, token, len, &err))
     756           2 :                                         break;
     757          30 :                                 if (wildcard != STATE_NONE) {
     758           0 :                                         lt_tag_fill_wildcard(tag, wildcard, tag->state - 1);
     759           0 :                                         wildcard = STATE_NONE;
     760             :                                 }
     761             :                         }
     762             :                 }
     763             :         }
     764          22 :         if (wildcard != STATE_NONE) {
     765           0 :                 lt_tag_fill_wildcard(tag, wildcard, STATE_END);
     766             :         }
     767          42 :         if (!err &&
     768          38 :             tag->state != STATE_PRE_EXTLANG &&
     769          36 :             tag->state != STATE_PRE_SCRIPT &&
     770          36 :             tag->state != STATE_PRE_REGION &&
     771          30 :             tag->state != STATE_PRE_VARIANT &&
     772          24 :             tag->state != STATE_PRE_EXTENSION &&
     773          24 :             tag->state != STATE_PRE_PRIVATEUSE &&
     774          24 :             tag->state != STATE_IN_EXTENSIONTOKEN &&
     775          24 :             tag->state != STATE_IN_PRIVATEUSETOKEN &&
     776          12 :             tag->state != STATE_NONE) {
     777          12 :                 lt_error_set(&err, LT_ERR_FAIL_ON_SCANNER,
     778             :                              "Invalid tag: %s, last token = '%s', state = %d, parsed count = %d",
     779          12 :                              langtag, token, tag->state, count);
     780             :         }
     781             :   bail:
     782          24 :         lt_tag_add_tag_string(tag, langtag);
     783          24 :         lt_tag_scanner_unref(scanner);
     784          24 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
     785          14 :                 if (error)
     786          14 :                         *error = lt_error_ref(err);
     787             :                 else
     788           0 :                         lt_error_print(err, LT_ERR_ANY);
     789          14 :                 lt_error_unref(err);
     790          14 :                 retval = FALSE;
     791             :         }
     792          24 :         if (token)
     793          22 :                 free(token);
     794             : 
     795          24 :         return retval;
     796             : }
     797             : 
     798             : static lt_bool_t
     799           0 : _lt_tag_match(const lt_tag_t *v1,
     800             :               lt_tag_t       *v2,
     801             :               lt_tag_state_t  state)
     802             : {
     803           0 :         lt_return_val_if_fail (v1 != NULL, FALSE);
     804           0 :         lt_return_val_if_fail (v2 != NULL, FALSE);
     805             : 
     806           0 :         if (state > STATE_EXTLANG && !v2->extlang && v1->extlang) {
     807           0 :                 lt_extlang_db_t *db = lt_db_get_extlang();
     808             : 
     809           0 :                 lt_tag_set_extlang(v2, lt_extlang_db_lookup(db, ""));
     810           0 :                 lt_extlang_db_unref(db);
     811             :         }
     812           0 :         if (state > STATE_SCRIPT && !v2->script && v1->script) {
     813           0 :                 lt_script_db_t *db = lt_db_get_script();
     814             : 
     815           0 :                 lt_tag_set_script(v2, lt_script_db_lookup(db, ""));
     816           0 :                 lt_script_db_unref(db);
     817             :         }
     818           0 :         if (state > STATE_REGION && !v2->region && v1->region) {
     819           0 :                 lt_region_db_t *db = lt_db_get_region();
     820             : 
     821           0 :                 lt_tag_set_region(v2, lt_region_db_lookup(db, ""));
     822           0 :                 lt_region_db_unref(db);
     823             :         }
     824           0 :         if (state > STATE_VARIANT && !v2->variants && v1->variants) {
     825           0 :                 lt_variant_db_t *db = lt_db_get_variant();
     826             : 
     827           0 :                 lt_tag_set_variant(v2, lt_variant_db_lookup(db, ""));
     828           0 :                 lt_variant_db_unref(db);
     829             :         }
     830           0 :         if (state > STATE_EXTENSION && !v2->extension && v1->extension) {
     831           0 :                 lt_extension_t *e = lt_extension_create();
     832             : 
     833           0 :                 lt_extension_add_singleton(e, ' ', NULL, NULL);
     834           0 :                 lt_tag_set_extension(v2, e);
     835             :         }
     836             : 
     837           0 :         return lt_tag_compare(v1, v2);
     838             : }
     839             : 
     840             : static void
     841           0 : _lt_tag_subtract(lt_tag_t       *tag,
     842             :                  const lt_tag_t *rtag)
     843             : {
     844           0 :         if (rtag->language) {
     845           0 :                 lt_tag_free_language(tag);
     846             :         }
     847           0 :         if (rtag->extlang) {
     848           0 :                 lt_tag_free_extlang(tag);
     849             :         }
     850           0 :         if (rtag->script) {
     851           0 :                 lt_tag_free_script(tag);
     852             :         }
     853           0 :         if (rtag->region) {
     854           0 :                 lt_tag_free_region(tag);
     855             :         }
     856           0 :         if (rtag->variants) {
     857           0 :                 lt_tag_free_variants(tag);
     858             :         }
     859           0 :         if (rtag->extension) {
     860             :                 /* XXX: how to deal with the multiple extensions? */
     861           0 :                 lt_tag_free_extension(tag);
     862             :         }
     863           0 :         if (rtag->privateuse) {
     864           0 :                 if (tag->privateuse)
     865           0 :                         lt_string_clear(tag->privateuse);
     866             :         }
     867           0 : }
     868             : 
     869             : static void
     870           0 : _lt_tag_replace(lt_tag_t       *tag,
     871             :                 const lt_tag_t *rtag)
     872             : {
     873           0 :         if (rtag->language) {
     874           0 :                 lt_return_if_fail (!tag->language);
     875           0 :                 lt_tag_set_language(tag, lt_lang_ref(rtag->language));
     876             :         }
     877           0 :         if (rtag->extlang) {
     878           0 :                 lt_return_if_fail (!tag->extlang);
     879           0 :                 lt_tag_set_extlang(tag, lt_extlang_ref(rtag->extlang));
     880             :         }
     881           0 :         if (rtag->script) {
     882           0 :                 lt_return_if_fail (!tag->script);
     883           0 :                 lt_tag_set_script(tag, lt_script_ref(rtag->script));
     884             :         }
     885           0 :         if (rtag->region) {
     886           0 :                 lt_return_if_fail (!tag->region);
     887           0 :                 lt_tag_set_region(tag, lt_region_ref(rtag->region));
     888             :         }
     889           0 :         if (rtag->variants) {
     890           0 :                 lt_list_t *l = rtag->variants;
     891             : 
     892           0 :                 lt_return_if_fail (!tag->variants);
     893             : 
     894           0 :                 while (l != NULL) {
     895           0 :                         lt_tag_set_variant(tag, lt_variant_ref(lt_list_value(l)));
     896           0 :                         l = lt_list_next(l);
     897             :                 }
     898             :         }
     899           0 :         if (rtag->extension) {
     900           0 :                 lt_return_if_fail (!tag->extension);
     901           0 :                 lt_tag_set_extension(tag, lt_extension_ref(rtag->extension));
     902             :         }
     903           0 :         if (rtag->privateuse) {
     904           0 :                 lt_string_clear(tag->privateuse);
     905           0 :                 lt_string_append(tag->privateuse, lt_string_value(rtag->privateuse));
     906             :         }
     907             : }
     908             : 
     909             : /* borrowed the modifier related code from localehelper:
     910             :  * http://people.redhat.com/caolanm/BCP47/localehelper-1.0.0.tar.gz
     911             :  */
     912             : /*
     913             :  * glibc typically uses these modifiers to indicate particular
     914             :  * scripts that the language is written in
     915             :  * See ISO-15924 http://unicode.org/iso15924/iso15924-codes.html
     916             :  */
     917             : static lt_bool_t
     918           0 : _lt_tag_convert_script_from_locale_modifier(const char  *modifier,
     919             :                                             const char **ret)
     920             : {
     921             :         /* XXX: think about how to get rid of the hardcoded mapping table */
     922             :         static const char * const maps[][2] = {
     923             :                 { "Arabic", "Arab" },
     924             :                 { "Imperial_Aramaic", "Armi" },
     925             :                 { "Armenian", "Armn" },
     926             :                 { "Avestan", "Avst" },
     927             :                 { "Balinese", "Bali" },
     928             :                 { "Bamum", "Bamu" },
     929             :                 { "Bengali", "Beng" },
     930             :                 { "Bopomofo", "Bopo" },
     931             :                 { "Braille", "Brai" },
     932             :                 { "Buginese", "Bugi" },
     933             :                 { "Buhid", "Buhd" },
     934             :                 { "Canadian_Aboriginal", "Cans" },
     935             :                 { "Carian", "Cari" },
     936             :                 { "Cham", "Cham" },
     937             :                 { "Cherokee", "Cher" },
     938             :                 { "Coptic", "Copt" },
     939             :                 { "Cypriot", "Cprt" },
     940             :                 { "Cyrillic", "Cyrl" },
     941             :                 { "Devanagari", "Deva" },
     942             :                 { "Deseret", "Dsrt" },
     943             :                 { "Egyptian_Hierogyphs", "Egyp" },
     944             :                 { "Ethiopic", "Ethi" },
     945             :                 { "Georgian", "Geor" },
     946             :                 { "Glagolitic", "Glag" },
     947             :                 { "Gothic", "Goth" },
     948             :                 { "Greek", "Grek" },
     949             :                 { "Gujarati", "Gujr" },
     950             :                 { "Gurmukhi", "Guru" },
     951             :                 { "Hangul", "Hang" },
     952             :                 { "Han", "Hani" },
     953             :                 { "Hanunoo", "Hano" },
     954             :                 { "Hebrew", "Hebr" },
     955             :                 { "Hiragana", "Hira" },
     956             :                 { "Katakana_Or_Hiragana", "Hrkt" },
     957             :                 { "Old_Italic", "Ital" },
     958             :                 { "Javanese", "Java" },
     959             :                 { "Kayah_Li", "Kali" },
     960             :                 { "Katakana", "Kana" },
     961             :                 { "Kharoshthi", "Khar" },
     962             :                 { "Khmer", "Khmr" },
     963             :                 { "Kannada", "Knda" },
     964             :                 { "Kaithi", "Kthi" },
     965             :                 { "Tai_Tham", "Lana" },
     966             :                 { "Lao", "Laoo" },
     967             :                 { "Latin", "Latn" },
     968             :                 { "Lepcha", "Lepc" },
     969             :                 { "Limbu", "Limb" },
     970             :                 { "Linear_B", "Linb" },
     971             :                 { "Lisu", "Lisu" },
     972             :                 { "Lycian", "Lyci" },
     973             :                 { "Lydian", "Lydi" },
     974             :                 { "Malayalam", "Mlym" },
     975             :                 { "Mongolian", "Mong" },
     976             :                 { "Meetei_Mayek", "Mtei" },
     977             :                 { "Myanmar", "Mymr" },
     978             :                 { "Nko", "Nkoo" },
     979             :                 { "Ogham", "Ogam" },
     980             :                 { "Ol_Chiki", "Olck" },
     981             :                 { "Old_Turkic", "Orkh" },
     982             :                 { "Oriya", "Orya" },
     983             :                 { "Osmanya", "Osma" },
     984             :                 { "Phags_Pa", "Phag" },
     985             :                 { "Inscriptional_Pahlavi", "Phli" },
     986             :                 { "Phoenician", "Phnx" },
     987             :                 { "Inscriptional_Parthian", "Prti" },
     988             :                 { "Rejang", "Rjng" },
     989             :                 { "Runic", "Runr" },
     990             :                 { "Samaritan", "Samr" },
     991             :                 { "Old_South_Arabian", "Sarb" },
     992             :                 { "Saurashtra", "Saur" },
     993             :                 { "Shavian", "Shaw" },
     994             :                 { "Sinhala", "Sinh" },
     995             :                 { "Sundanese", "Sund" },
     996             :                 { "Syloti_Nagri", "Sylo" },
     997             :                 { "Syriac", "Syrc" },
     998             :                 { "Tagbanwa", "Tagb" },
     999             :                 { "Tai_Le", "Tale" },
    1000             :                 { "New_Tai_Lue", "Talu" },
    1001             :                 { "Tamil", "Taml" },
    1002             :                 { "Tai_Viet", "Tavt" },
    1003             :                 { "Telugu", "Telu" },
    1004             :                 { "Tifinagh", "Tfng" },
    1005             :                 { "Tagalog", "Tglg" },
    1006             :                 { "Thaana", "Thaa" },
    1007             :                 { "Thai", "Thai" },
    1008             :                 { "Tibetan", "Tibt" },
    1009             :                 { "Ugaritic", "Ugar" },
    1010             :                 { "Vai", "Vaii" },
    1011             :                 { "Old_Persian", "Xpeo" },
    1012             :                 { "Cuneiform", "Xsux" },
    1013             :                 { "Yi", "Yiii" },
    1014             :                 { "Inherited", "Zinh" },
    1015             :                 { "Common", "Zyyy" },
    1016             :                 { "Unknown", "Zzzz" },
    1017             :         };
    1018             :         size_t i;
    1019             : 
    1020           0 :         if (modifier) {
    1021             :                 /*
    1022             :                  * Special case this one. The script is definitely Latin
    1023             :                  * and not Cyrillic. But lets bubble the transliteration scheme
    1024             :                  * through another layer with return 0
    1025             :                  */
    1026           0 :                 if (lt_strcasecmp(modifier, "iqtelif") == 0) {
    1027           0 :                         _lt_tag_convert_script_from_locale_modifier("Latin", ret);
    1028           0 :                         return FALSE;
    1029             :                 }
    1030           0 :                 for (i = 0; i < sizeof (maps) / sizeof (char *[2]); i++) {
    1031           0 :                         if (lt_strcasecmp(modifier, maps[i][0]) == 0) {
    1032           0 :                                 *ret = maps[i][1];
    1033           0 :                                 return TRUE;
    1034             :                         }
    1035             :                 }
    1036             :         }
    1037             : 
    1038           0 :         return FALSE;
    1039             : }
    1040             : 
    1041             : /*
    1042             :  * Occasionally (ca_ES@valencia) some modifiers indicate a language variant
    1043             :  * See http://www.iana.org/assignments/language-subtag-registry
    1044             :  * for IANA language subtag assignments output codes
    1045             :  */
    1046             : static lt_bool_t
    1047           0 : _lt_tag_convert_variant_from_locale_modifier(const char  *modifier,
    1048             :                                              const char **ret)
    1049             : {
    1050             :         /* XXx: think about how to get rid of the hardcoded mapping table */
    1051             :         static const char * const maps[][2] = {
    1052             :             { "valencia", "valencia" }
    1053             :         };
    1054             :         size_t i;
    1055             : 
    1056           0 :         if (modifier) {
    1057           0 :                 for (i = 0; i < sizeof (maps) / sizeof (char *[2]); i++) {
    1058           0 :                         if (lt_strcasecmp(modifier, maps[i][0]) == 0) {
    1059           0 :                                 *ret = maps[i][1];
    1060           0 :                                 return TRUE;
    1061             :                         }
    1062             :                 }
    1063             :         }
    1064             : 
    1065           0 :         return FALSE;
    1066             : }
    1067             : 
    1068             : static const char * const
    1069           0 : _lt_tag_convert_privaseuse_from_locale_modifier(const char *modifier)
    1070             : {
    1071             :         /* XXX: think about how to get rid of the hardcoded mapping table */
    1072             :         static const char * const maps[][2] = {
    1073             :                 /*
    1074             :                  * Old mechanism to denote that the euro currency is in use, 
    1075             :                  * ignore it.
    1076             :                  */
    1077             :                 { "euro", NULL },
    1078             :                 /*
    1079             :                  * http://www.mail-archive.com/cygwin@cygwin.com/msg97848.html
    1080             :                  * 
    1081             :                  * A modifier that indicates what width to assign to an
    1082             :                  * ambiguous width char, ignore it.
    1083             :                  *
    1084             :                  * http://unicode.org/reports/tr11/
    1085             :                  */
    1086             :                 { "cjknarrow", NULL },
    1087             :                 /*
    1088             :                  * http://www.geez.org/Collation/
    1089             :                  * 
    1090             :                  * Abegede Collation for Ge'ez (as opposed to Halehame, I believe)
    1091             :                  *
    1092             :                  * http://www.iana.org/assignments/language-subtag-registry has
    1093             :                  * nothing to describe it, so using a private code
    1094             :                  *
    1095             :                  * http://tools.ietf.org/html/draft-davis-u-langtag-ext-01
    1096             :                  * http://www.unicode.org/reports/tr35/ maybe u-co-something some day
    1097             :                  */
    1098             :                 { "abegede", "abegede" },
    1099             :                 /*
    1100             :                  * http://www.alvestrand.no/pipermail/ietf-languages/2006-September/005017.html
    1101             :                  * 
    1102             :                  * "iqtelif" Latin orthography
    1103             :                  *
    1104             :                  * Bit of a mess really. Unsure if tt-Latn is sufficient, i.e. if this is
    1105             :                  * the default latin orghography in practice but a private code
    1106             :                  * doesn't hurt I guess
    1107             :                  */
    1108             :                 { "iqtelif", "iqtel" }
    1109             :         };
    1110             :         size_t i;
    1111             : 
    1112           0 :         if (modifier) {
    1113           0 :                 for (i = 0; i < sizeof (maps) / sizeof (char *[2]); i++) {
    1114           0 :                         if (lt_strcasecmp(modifier, maps[i][0]) == 0)
    1115           0 :                                 return maps[i][1];
    1116             :                 }
    1117             : 
    1118           0 :                 lt_warning("Unknown modifiers: %s", modifier);
    1119             : 
    1120           0 :                 return modifier;
    1121             :         }
    1122             : 
    1123           0 :         return NULL;
    1124             : }
    1125             : 
    1126             : static lt_tag_t *
    1127           0 : _lt_tag_convert_from_locale_string(const char  *locale,
    1128             :                                    lt_error_t **error)
    1129             : {
    1130             :         char *s, *territory, *codeset, *modifier;
    1131             :         lt_tag_t *tag;
    1132           0 :         lt_error_t *err = NULL;
    1133             : 
    1134           0 :         s = strdup(locale);
    1135           0 :         tag = lt_tag_new();
    1136           0 :         if (!s || s[0] == 0 ||
    1137           0 :             lt_strcmp0(s, "C") == 0 ||
    1138           0 :             lt_strcmp0(s, "POSIX") == 0) {
    1139           0 :                 if (!lt_tag_parse(tag, "en-US-u-va-posix", &err))
    1140           0 :                         goto bail;
    1141             :         } else {
    1142             :                 lt_string_t *tag_string;
    1143           0 :                 const char *script = NULL, *variant = NULL, *privateuse = NULL;
    1144             : 
    1145           0 :                 modifier = strchr(s, '@');
    1146           0 :                 if (modifier) {
    1147           0 :                         *modifier = 0;
    1148           0 :                         modifier++;
    1149             :                 }
    1150           0 :                 codeset = strchr(s, '.');
    1151           0 :                 if (codeset) {
    1152           0 :                         *codeset = 0;
    1153           0 :                         codeset++;
    1154             :                 }
    1155           0 :                 territory = strchr(s, '_');
    1156           0 :                 if (territory) {
    1157           0 :                         *territory = 0;
    1158           0 :                         territory++;
    1159             :                 }
    1160           0 :                 if (codeset &&
    1161           0 :                     (lt_strcasecmp(codeset, "utf-8") == 0 ||
    1162           0 :                      lt_strcasecmp(codeset, "utf8") == 0)) {
    1163           0 :                         codeset = NULL;
    1164             :                 }
    1165             :                 /* check if the language is a locale alias */
    1166           0 :                 if (strlen(s) > 3 &&
    1167           0 :                     !territory &&
    1168           0 :                     !codeset &&
    1169             :                     !modifier) {
    1170           0 :                         const char *loc = lt_tag_get_locale_from_locale_alias(s);
    1171             :                         lt_tag_t *t;
    1172             : 
    1173           0 :                         if (loc && (t = _lt_tag_convert_from_locale_string(loc, &err)) != NULL) {
    1174           0 :                                 lt_tag_unref(tag);
    1175           0 :                                 tag = t;
    1176             :                                 goto bail;
    1177             :                         }
    1178             :                 }
    1179           0 :                 if (!_lt_tag_convert_script_from_locale_modifier(modifier, &script))
    1180           0 :                         if (!_lt_tag_convert_variant_from_locale_modifier(modifier, &variant))
    1181           0 :                                 privateuse = _lt_tag_convert_privaseuse_from_locale_modifier(modifier);
    1182             : 
    1183           0 :                 tag_string = lt_string_new(s);
    1184           0 :                 if (script)
    1185           0 :                         lt_string_append_printf(tag_string, "-%s", script);
    1186           0 :                 if (territory)
    1187           0 :                         lt_string_append_printf(tag_string, "-%s", territory);
    1188           0 :                 if (variant)
    1189           0 :                         lt_string_append_printf(tag_string, "-%s", variant);
    1190           0 :                 if (codeset || privateuse) {
    1191           0 :                         lt_string_append(tag_string, "-x");
    1192           0 :                         if (codeset)
    1193           0 :                                 lt_string_append_printf(tag_string, "-codeset-%s", codeset);
    1194           0 :                         if (privateuse)
    1195           0 :                                 lt_string_append_printf(tag_string, "-%s", privateuse);
    1196             :                 }
    1197           0 :                 if (!lt_tag_parse(tag, lt_string_value(tag_string), &err)) {
    1198           0 :                         lt_string_unref(tag_string);
    1199             :                         goto bail;
    1200             :                 }
    1201           0 :                 lt_string_unref(tag_string);
    1202             :         }
    1203             : 
    1204             :   bail:
    1205           0 :         if (s)
    1206           0 :                 free(s);
    1207             : 
    1208           0 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    1209           0 :                 if (error)
    1210           0 :                         *error = lt_error_ref(err);
    1211             :                 else
    1212           0 :                         lt_error_print(err, LT_ERR_ANY);
    1213           0 :                 lt_error_unref(err);
    1214           0 :                 lt_tag_unref(tag);
    1215           0 :                 tag = NULL;
    1216             :         }
    1217             : 
    1218           0 :         return tag;
    1219             : }
    1220             : 
    1221             : /*< protected >*/
    1222             : lt_tag_state_t
    1223           0 : lt_tag_parse_wildcard(lt_tag_t    *tag,
    1224             :                       const char  *tag_string,
    1225             :                       lt_error_t **error)
    1226             : {
    1227           0 :         lt_error_t *err = NULL;
    1228             :         lt_bool_t ret;
    1229             : 
    1230           0 :         lt_tag_parser_init(tag);
    1231           0 :         ret = _lt_tag_parse(tag, tag_string, TRUE, &err);
    1232             : 
    1233           0 :         if (!ret && !err) {
    1234           0 :                 lt_error_set(&err, LT_ERR_FAIL_ON_SCANNER,
    1235             :                              "Unknown error during parsing a tag.");
    1236             :         }
    1237           0 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    1238           0 :                 if (error)
    1239           0 :                         *error = lt_error_ref(err);
    1240             :                 else
    1241           0 :                         lt_error_print(err, LT_ERR_ANY);
    1242           0 :                 lt_error_unref(err);
    1243             :         }
    1244             : 
    1245           0 :         return tag->state;
    1246             : }
    1247             : 
    1248             : /*< public >*/
    1249             : /**
    1250             :  * lt_tag_new:
    1251             :  *
    1252             :  * Create a new instance of #lt_tag_t.
    1253             :  *
    1254             :  * Returns: (transfer full): a new instance of #lt_tag_t.
    1255             :  */
    1256             : lt_tag_t *
    1257          24 : lt_tag_new(void)
    1258             : {
    1259          24 :         lt_tag_t *retval = lt_mem_alloc_object(sizeof (lt_tag_t));
    1260             : 
    1261          24 :         if (retval) {
    1262          24 :                 retval->state = STATE_NONE;
    1263          24 :                 retval->privateuse = lt_string_new(NULL);
    1264          24 :                 lt_mem_add_ref(&retval->parent, retval->privateuse,
    1265             :                                (lt_destroy_func_t)lt_string_unref);
    1266             :         }
    1267             : 
    1268          24 :         return retval;
    1269             : }
    1270             : 
    1271             : /**
    1272             :  * lt_tag_ref:
    1273             :  * @tag: a #lt_tag_t.
    1274             :  *
    1275             :  * Increases the reference count of @tag.
    1276             :  *
    1277             :  * Returns: (transfer none): the same @tag object.
    1278             :  */
    1279             : lt_tag_t *
    1280           0 : lt_tag_ref(lt_tag_t *tag)
    1281             : {
    1282           0 :         lt_return_val_if_fail (tag != NULL, NULL);
    1283             : 
    1284           0 :         return lt_mem_ref(&tag->parent);
    1285             : }
    1286             : 
    1287             : /**
    1288             :  * lt_tag_unref:
    1289             :  * @tag: a #lt_tag_t.
    1290             :  *
    1291             :  * Decreases the reference count of @tag. when its reference count
    1292             :  * drops to 0, the object is finalized (i.e. its memory is freed).
    1293             :  */
    1294             : void
    1295          24 : lt_tag_unref(lt_tag_t *tag)
    1296             : {
    1297          24 :         if (tag)
    1298          24 :                 lt_mem_unref(&tag->parent);
    1299          24 : }
    1300             : 
    1301             : /**
    1302             :  * lt_tag_clear:
    1303             :  * @tag: a #lt_tag_t.
    1304             :  *
    1305             :  * (Re-)Initialize all of the subtag information stored in @tag.
    1306             :  */
    1307             : void
    1308          24 : lt_tag_clear(lt_tag_t *tag)
    1309             : {
    1310          48 :         lt_return_if_fail (tag != NULL);
    1311             : 
    1312          24 :         lt_tag_free_tag_string(tag);
    1313          24 :         lt_tag_free_language(tag);
    1314          24 :         lt_tag_free_extlang(tag);
    1315          24 :         lt_tag_free_script(tag);
    1316          24 :         lt_tag_free_region(tag);
    1317          24 :         lt_tag_free_variants(tag);
    1318          24 :         lt_tag_free_extension(tag);
    1319          24 :         if (tag->privateuse) {
    1320          24 :                 lt_string_clear(tag->privateuse);
    1321             :         }
    1322          24 :         lt_tag_free_grandfathered(tag);
    1323             : }
    1324             : 
    1325             : /**
    1326             :  * lt_tag_parse:
    1327             :  * @tag: a #lt_tag_t.
    1328             :  * @tag_string: language tag to be parsed.
    1329             :  * @error: (allow-none): a #lt_error_t or %NULL.
    1330             :  *
    1331             :  * Parse @tag_string and create appropriate instances for subtags.
    1332             :  *
    1333             :  * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
    1334             :  */
    1335             : lt_bool_t
    1336          24 : lt_tag_parse(lt_tag_t    *tag,
    1337             :              const char  *tag_string,
    1338             :              lt_error_t **error)
    1339             : {
    1340          24 :         lt_tag_parser_init(tag);
    1341             : 
    1342          24 :         return _lt_tag_parse(tag, tag_string, FALSE, error);
    1343             : }
    1344             : 
    1345             : /**
    1346             :  * lt_tag_parse_with_extra_token:
    1347             :  * @tag: a #lt_tag_t.
    1348             :  * @tag_string: a language tag to be parsed much more.
    1349             :  * @error: (allow-none): a #lt_error_t or %NULL.
    1350             :  *
    1351             :  * Continue to parse a language tag with @tag_string. please use lt_tag_parse()
    1352             :  * at first.
    1353             :  *
    1354             :  * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
    1355             :  */
    1356             : lt_bool_t
    1357           0 : lt_tag_parse_with_extra_token(lt_tag_t    *tag,
    1358             :                               const char  *tag_string,
    1359             :                               lt_error_t **error)
    1360             : {
    1361           0 :         lt_return_val_if_fail (tag != NULL, FALSE);
    1362           0 :         lt_return_val_if_fail (tag->state != STATE_NONE, FALSE);
    1363             : 
    1364           0 :         return _lt_tag_parse(tag, tag_string, FALSE, error);
    1365             : }
    1366             : 
    1367             : /**
    1368             :  * lt_tag_copy:
    1369             :  * @tag: a #lt_tag_t.
    1370             :  *
    1371             :  * Create a copy instance of @tag.
    1372             :  *
    1373             :  * Returns: (transfer full): a new instance of #lt_tag_t or %NULL if fails.
    1374             :  */
    1375             : lt_tag_t *
    1376           4 : lt_tag_copy(const lt_tag_t *tag)
    1377             : {
    1378             :         lt_tag_t *retval;
    1379             : 
    1380           4 :         lt_return_val_if_fail (tag != NULL, NULL);
    1381             : 
    1382           4 :         retval = lt_tag_new();
    1383           4 :         retval->wildcard_map = tag->wildcard_map;
    1384           4 :         retval->state = tag->state;
    1385           4 :         if (tag->language) {
    1386           4 :                 lt_tag_set_language(retval, lt_lang_ref(tag->language));
    1387             :         }
    1388           4 :         if (tag->extlang) {
    1389           0 :                 lt_tag_set_extlang(retval, lt_extlang_ref(tag->extlang));
    1390             :         }
    1391           4 :         if (tag->script) {
    1392           4 :                 lt_tag_set_script(retval, lt_script_ref(tag->script));
    1393             :         }
    1394           4 :         if (tag->region) {
    1395           4 :                 lt_tag_set_region(retval, lt_region_ref(tag->region));
    1396             :         }
    1397           4 :         if (tag->variants) {
    1398             :                 lt_list_t *l;
    1399             : 
    1400           0 :                 for (l = tag->variants; l != NULL; l = lt_list_next(l)) {
    1401           0 :                         retval->variants = lt_list_append(retval->variants,
    1402           0 :                                                           lt_variant_ref(lt_list_value(l)),
    1403             :                                                           (lt_destroy_func_t)lt_variant_unref);
    1404             :                 }
    1405             :         }
    1406           4 :         if (tag->extension) {
    1407           0 :                 lt_tag_set_extension(retval, lt_extension_copy(tag->extension));
    1408             :         }
    1409           4 :         if (tag->privateuse) {
    1410           4 :                 lt_string_append(retval->privateuse, lt_string_value(tag->privateuse));
    1411             :         }
    1412           4 :         if (tag->grandfathered) {
    1413           0 :                 lt_tag_set_grandfathered(retval, lt_grandfathered_ref(tag->grandfathered));
    1414             :         }
    1415             : 
    1416           4 :         return retval;
    1417             : }
    1418             : 
    1419             : /**
    1420             :  * lt_tag_truncate:
    1421             :  * @tag: a #lt_tag_t.
    1422             :  * @error: (allow-none): a #lt_error_t.
    1423             :  *
    1424             :  * Truncate the last subtag.
    1425             :  *
    1426             :  * Returns: %TRUE if a subtag was truncated, otherwise %FALSE.
    1427             :  */
    1428             : lt_bool_t
    1429           8 : lt_tag_truncate(lt_tag_t    *tag,
    1430             :                 lt_error_t **error)
    1431             : {
    1432           8 :         lt_error_t *err = NULL;
    1433           8 :         lt_bool_t retval = TRUE;
    1434             : 
    1435           8 :         lt_return_val_if_fail (tag != NULL, FALSE);
    1436             : 
    1437           8 :         if (tag->grandfathered) {
    1438           0 :                 lt_error_set(&err, LT_ERR_NO_TAG,
    1439             :                              "Grandfathered subtag can't be truncated.");
    1440           0 :                 goto bail;
    1441             :         }
    1442             :         while (1) {
    1443           8 :                 if (tag->privateuse && lt_string_length(tag->privateuse) > 0) {
    1444           0 :                         lt_string_clear(tag->privateuse);
    1445           0 :                         break;
    1446             :                 }
    1447           8 :                 if (tag->extension) {
    1448             :                         int i;
    1449             :                         char c;
    1450           0 :                         lt_bool_t has_tag = FALSE;
    1451             : 
    1452           0 :                         lt_extension_truncate(tag->extension);
    1453           0 :                         for (i = 0; i < LT_MAX_EXT_MODULES; i++) {
    1454           0 :                                 c = lt_ext_module_singleton_int_to_char(i);
    1455             : 
    1456           0 :                                 if (c == 'x')
    1457           0 :                                         continue;
    1458           0 :                                 has_tag = lt_extension_has_singleton(tag->extension, c);
    1459           0 :                                 if (has_tag)
    1460           0 :                                         break;
    1461             :                         }
    1462           0 :                         if (!has_tag) {
    1463           0 :                                 lt_tag_free_extension(tag);
    1464             :                         }
    1465           0 :                         break;
    1466             :                 }
    1467           8 :                 if (tag->variants) {
    1468           0 :                         lt_list_t *l = lt_list_last(tag->variants);
    1469             : 
    1470           0 :                         if (tag->variants == l) {
    1471           0 :                                 lt_mem_remove_ref(&tag->parent, tag->variants);
    1472           0 :                                 tag->variants = lt_list_delete_link(tag->variants, l);
    1473           0 :                                 if (tag->variants)
    1474           0 :                                         lt_mem_add_ref(&tag->parent, tag->variants, lt_list_free);
    1475             :                         } else {
    1476           0 :                                 l = lt_list_delete_link(l, l);
    1477             :                         }
    1478           0 :                         break;
    1479             :                 }
    1480           8 :                 if (tag->region) {
    1481           4 :                         lt_tag_free_region(tag);
    1482           4 :                         break;
    1483             :                 }
    1484           4 :                 if (tag->script) {
    1485           2 :                         lt_tag_free_script(tag);
    1486           2 :                         break;
    1487             :                 }
    1488           2 :                 if (tag->extlang) {
    1489           0 :                         lt_tag_free_extlang(tag);
    1490           0 :                         break;
    1491             :                 }
    1492           2 :                 if (tag->language) {
    1493           2 :                         lt_tag_free_language(tag);
    1494           2 :                         break;
    1495             :                 }
    1496           0 :                 lt_error_set(&err, LT_ERR_NO_TAG,
    1497             :                              "No tags to be truncated.");
    1498           0 :                 goto bail;
    1499             :         }
    1500           8 :         lt_tag_free_tag_string(tag);
    1501             :   bail:
    1502           8 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    1503           0 :                 if (error)
    1504           0 :                         *error = lt_error_ref(err);
    1505             :                 else
    1506           0 :                         lt_error_print(err, LT_ERR_ANY);
    1507           0 :                 lt_error_unref(err);
    1508           0 :                 retval = FALSE;
    1509             :         }
    1510             : 
    1511           8 :         return retval;
    1512             : }
    1513             : 
    1514             : /**
    1515             :  * lt_tag_get_string:
    1516             :  * @tag: a #lt_tag_t.
    1517             :  *
    1518             :  * Obtains a language tag in string.
    1519             :  *
    1520             :  * Returns: a language tag string.
    1521             :  */
    1522             : const char *
    1523          12 : lt_tag_get_string(lt_tag_t *tag)
    1524             : {
    1525             :         lt_list_t *l;
    1526             : 
    1527          12 :         if (tag->tag_string)
    1528           0 :                 return lt_string_value(tag->tag_string);
    1529             : 
    1530          12 :         if (tag->grandfathered)
    1531           0 :                 lt_tag_add_tag_string(tag, lt_grandfathered_get_tag(tag->grandfathered));
    1532          12 :         else if (tag->language) {
    1533          10 :                 lt_tag_add_tag_string(tag, lt_lang_get_tag(tag->language));
    1534          10 :                 if (tag->extlang)
    1535           0 :                         lt_tag_add_tag_string(tag, lt_extlang_get_tag(tag->extlang));
    1536          10 :                 if (tag->script)
    1537           8 :                         lt_tag_add_tag_string(tag, lt_script_get_tag(tag->script));
    1538          10 :                 if (tag->region)
    1539           4 :                         lt_tag_add_tag_string(tag, lt_region_get_tag(tag->region));
    1540          10 :                 l = tag->variants;
    1541          20 :                 while (l != NULL) {
    1542           0 :                         lt_tag_add_tag_string(tag, lt_variant_get_tag(lt_list_value(l)));
    1543           0 :                         l = lt_list_next(l);
    1544             :                 }
    1545          10 :                 if (tag->extension)
    1546           0 :                         lt_tag_add_tag_string(tag, lt_extension_get_tag(tag->extension));
    1547          10 :                 if (tag->privateuse && lt_string_length(tag->privateuse) > 0)
    1548           0 :                         lt_tag_add_tag_string(tag, lt_string_value(tag->privateuse));
    1549           2 :         } else if (tag->privateuse && lt_string_length(tag->privateuse) > 0) {
    1550           0 :                 lt_tag_add_tag_string(tag, lt_string_value(tag->privateuse));
    1551             :         } else {
    1552           2 :                 return NULL;
    1553             :         }
    1554             : 
    1555          10 :         return lt_string_value(tag->tag_string);
    1556             : }
    1557             : 
    1558             : /**
    1559             :  * lt_tag_canonicalize:
    1560             :  * @tag: a #lt_tag_t.
    1561             :  * @error: (allow-none): a #lt_error_t or %NULL.
    1562             :  *
    1563             :  * Canonicalize the language tag according to various information of subtags.
    1564             :  *
    1565             :  * Returns: a language tag string.
    1566             :  */
    1567             : char *
    1568           6 : lt_tag_canonicalize(lt_tag_t    *tag,
    1569             :                     lt_error_t **error)
    1570             : {
    1571           6 :         char *retval = NULL;
    1572           6 :         lt_string_t *string = NULL;
    1573           6 :         lt_error_t *err = NULL;
    1574             :         lt_list_t *l;
    1575           6 :         lt_redundant_db_t *rdb = NULL;
    1576           6 :         lt_redundant_t *r = NULL;
    1577           6 :         lt_tag_t *ctag = NULL;
    1578             : 
    1579           6 :         lt_return_val_if_fail (tag != NULL, NULL);
    1580             : 
    1581           6 :         string = lt_string_new(NULL);
    1582           6 :         if (tag->grandfathered) {
    1583           2 :                 lt_string_append(string, lt_grandfathered_get_better_tag(tag->grandfathered));
    1584           2 :                 goto bail1;
    1585             :         }
    1586             : 
    1587           4 :         ctag = lt_tag_copy(tag);
    1588           4 :         rdb = lt_db_get_redundant();
    1589             :         while (1) {
    1590          12 :                 const char *tag_string = lt_tag_get_string(ctag);
    1591             : 
    1592          12 :                 if (tag_string == NULL || tag_string[0] == 0)
    1593             :                         break;
    1594          10 :                 if (r)
    1595           0 :                         lt_redundant_unref(r);
    1596          10 :                 r = lt_redundant_db_lookup(rdb, tag_string);
    1597          10 :                 if (r) {
    1598           2 :                         const char *preferred = lt_redundant_get_preferred_tag(r);
    1599             : 
    1600           2 :                         if (preferred) {
    1601           0 :                                 lt_tag_t *rtag = lt_tag_new();
    1602           0 :                                 lt_tag_t *ntag = lt_tag_new();
    1603             : 
    1604           0 :                                 if (!lt_tag_parse(rtag, lt_redundant_get_tag(r), &err)) {
    1605           0 :                                         lt_tag_unref(rtag);
    1606           0 :                                         lt_tag_unref(ntag);
    1607           0 :                                         goto bail1;
    1608             :                                 }
    1609           0 :                                 if (!lt_tag_parse(ntag, preferred, &err)) {
    1610           0 :                                         lt_tag_unref(rtag);
    1611           0 :                                         lt_tag_unref(ntag);
    1612           0 :                                         goto bail1;
    1613             :                                 }
    1614           0 :                                 _lt_tag_subtract(tag, rtag);
    1615           0 :                                 _lt_tag_replace(tag, ntag);
    1616           0 :                                 lt_tag_unref(rtag);
    1617           0 :                                 lt_tag_unref(ntag);
    1618             :                         }
    1619           2 :                         break;
    1620             :                 } else {
    1621           8 :                         if (!lt_tag_truncate(ctag, &err))
    1622           0 :                                 goto bail1;
    1623             :                 }
    1624           8 :         }
    1625             : 
    1626           4 :         if (tag->language) {
    1627             :                 size_t len;
    1628           4 :                 lt_extlang_db_t *edb = lt_db_get_extlang();
    1629             :                 lt_extlang_t *e;
    1630             : 
    1631             :                 /* If the language tag starts with a primary language subtag
    1632             :                  * that is also an extlang subtag, then the language tag is
    1633             :                  * prepended with the extlang's 'Prefix'.
    1634             :                  */
    1635           4 :                 e = lt_extlang_db_lookup(edb, lt_lang_get_better_tag(tag->language));
    1636           4 :                 if (e) {
    1637           0 :                         const char *prefix = lt_extlang_get_prefix(e);
    1638             : 
    1639           0 :                         if (prefix)
    1640           0 :                                 lt_string_append_printf(string, "%s-", prefix);
    1641           0 :                         lt_extlang_unref(e);
    1642             :                 }
    1643           4 :                 lt_extlang_db_unref(edb);
    1644             : 
    1645           4 :                 lt_string_append(string, lt_lang_get_better_tag(tag->language));
    1646           4 :                 if (tag->extlang) {
    1647           0 :                         const char *preferred = lt_extlang_get_preferred_tag(tag->extlang);
    1648             : 
    1649           0 :                         if (preferred) {
    1650           0 :                                 lt_string_clear(string);
    1651           0 :                                 lt_string_append(string, preferred);
    1652             :                         } else {
    1653           0 :                                 lt_string_append_printf(string, "-%s",
    1654           0 :                                                         lt_extlang_get_tag(tag->extlang));
    1655             :                         }
    1656             :                 }
    1657           4 :                 if (tag->script) {
    1658           4 :                         const char *script = lt_script_get_tag(tag->script);
    1659           4 :                         const char *suppress = lt_lang_get_suppress_script(tag->language);
    1660             : 
    1661           6 :                         if (!suppress ||
    1662           2 :                             lt_strcasecmp(suppress, script))
    1663           2 :                                 lt_string_append_printf(string, "-%s", script);
    1664             :                 }
    1665           4 :                 if (tag->region) {
    1666           4 :                         lt_string_append_printf(string, "-%s", lt_region_get_better_tag(tag->region));
    1667             :                 }
    1668           4 :                 len = lt_string_length(string);
    1669           4 :                 for (l = tag->variants; l != NULL; l = lt_list_next(l)) {
    1670           0 :                         lt_variant_t *variant = lt_list_value(l);
    1671           0 :                         const char *better = lt_variant_get_better_tag(variant);
    1672           0 :                         const char *s = lt_variant_get_tag(variant);
    1673             : 
    1674           0 :                         if (better && lt_strcasecmp(s, better) != 0) {
    1675             :                                 /* ignore all of variants prior to this one */
    1676           0 :                                 lt_string_truncate(string, len);
    1677             :                         }
    1678           0 :                         lt_string_append_printf(string, "-%s", better ? better : s);
    1679             :                 }
    1680           4 :                 if (tag->extension) {
    1681           0 :                         char *s = lt_extension_get_canonicalized_tag(tag->extension);
    1682             : 
    1683           0 :                         lt_string_append_printf(string, "-%s", s);
    1684           0 :                         free(s);
    1685             :                 }
    1686             :         }
    1687           4 :         if (tag->privateuse && lt_string_length(tag->privateuse) > 0) {
    1688           0 :                 lt_string_append_printf(string, "%s%s",
    1689           0 :                                         lt_string_length(string) > 0 ? "-" : "",
    1690           0 :                                         lt_string_value(tag->privateuse));
    1691             :         }
    1692           4 :         if (lt_string_length(string) == 0) {
    1693           0 :                 lt_error_set(&err, LT_ERR_NO_TAG,
    1694             :                              "No tag to convert.");
    1695             :         }
    1696             :   bail1:
    1697           6 :         if (ctag)
    1698           4 :                 lt_tag_unref(ctag);
    1699           6 :         if (rdb)
    1700           4 :                 lt_redundant_db_unref(rdb);
    1701           6 :         if (r)
    1702           2 :                 lt_redundant_unref(r);
    1703           6 :         retval = lt_string_free(string, FALSE);
    1704           6 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    1705           0 :                 if (error)
    1706           0 :                         *error = lt_error_ref(err);
    1707             :                 else
    1708           0 :                         lt_error_print(err, LT_ERR_ANY);
    1709           0 :                 lt_error_unref(err);
    1710           0 :                 if (retval)
    1711           0 :                         free(retval);
    1712           0 :                 retval = NULL;
    1713             :         }
    1714             : 
    1715           6 :         return retval;
    1716             : }
    1717             : 
    1718             : /**
    1719             :  * lt_tag_convert_from_locale:
    1720             :  * @error: (allow-none): a #lt_error_t.
    1721             :  *
    1722             :  * Convert current locale to the language tag.
    1723             :  *
    1724             :  * Returns: (transfer full): a #lt_tag_t, %NULL if fails.
    1725             :  */
    1726             : lt_tag_t *
    1727           0 : lt_tag_convert_from_locale(lt_error_t **error)
    1728             : {
    1729             :         const char *locale;
    1730             : 
    1731           0 :         locale = setlocale(LC_CTYPE, NULL);
    1732           0 :         if (!locale)
    1733           0 :                 locale = setlocale(LC_ALL, NULL);
    1734           0 :         return _lt_tag_convert_from_locale_string(locale, error);
    1735             : }
    1736             : 
    1737             : /**
    1738             :  * lt_tag_convert_to_locale:
    1739             :  * @tag: a #lt_tag_t.
    1740             :  * @error: (allow-none): a #lt_error_t or %NULL.
    1741             :  *
    1742             :  * Convert the language tag to the locale.
    1743             :  *
    1744             :  * Returns: a locale string or %NULL if fails
    1745             :  */
    1746             : char *
    1747           0 : lt_tag_convert_to_locale(lt_tag_t    *tag,
    1748             :                          lt_error_t **error)
    1749             : {
    1750           0 :         char *retval = NULL;
    1751           0 :         lt_string_t *string = NULL;
    1752           0 :         lt_error_t *err = NULL;
    1753           0 :         const char *mod = NULL;
    1754           0 :         char *canonical_tag = NULL;
    1755             :         lt_tag_t *ctag;
    1756             : 
    1757           0 :         lt_return_val_if_fail (tag != NULL, NULL);
    1758             : 
    1759           0 :         canonical_tag = lt_tag_canonicalize(tag, &err);
    1760           0 :         if (!canonical_tag)
    1761           0 :                 goto bail;
    1762           0 :         ctag = lt_tag_new();
    1763           0 :         if (!lt_tag_parse(ctag, canonical_tag, &err)) {
    1764           0 :                 lt_tag_unref(ctag);
    1765           0 :                 goto bail;
    1766             :         }
    1767           0 :         string = lt_string_new(NULL);
    1768           0 :         lt_string_append(string, lt_lang_get_better_tag(ctag->language));
    1769           0 :         if (ctag->region)
    1770           0 :                 lt_string_append_printf(string, "_%s",
    1771           0 :                                         lt_region_get_tag(ctag->region));
    1772           0 :         if (ctag->script) {
    1773           0 :                 mod = lt_script_convert_to_modifier(ctag->script);
    1774           0 :                 if (mod)
    1775           0 :                         lt_string_append_printf(string, "@%s", mod);
    1776             :         }
    1777           0 :         lt_tag_unref(ctag);
    1778             : 
    1779             :   bail:
    1780           0 :         if (canonical_tag)
    1781           0 :                 free(canonical_tag);
    1782           0 :         if (string)
    1783           0 :                 retval = lt_string_free(string, FALSE);
    1784           0 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    1785           0 :                 if (error)
    1786           0 :                         *error = lt_error_ref(err);
    1787             :                 else
    1788           0 :                         lt_error_print(err, LT_ERR_ANY);
    1789           0 :                 lt_error_unref(err);
    1790           0 :                 if (retval)
    1791           0 :                         free(retval);
    1792           0 :                 retval = NULL;
    1793             :         }
    1794             : 
    1795           0 :         return retval;
    1796             : }
    1797             : 
    1798             : /**
    1799             :  * lt_tag_dump:
    1800             :  * @tag: a #lt_tag_t.
    1801             :  *
    1802             :  * Dumps the container information to the standard output.
    1803             :  */
    1804             : void
    1805           0 : lt_tag_dump(const lt_tag_t *tag)
    1806             : {
    1807             :         lt_list_t *l;
    1808             : 
    1809           0 :         lt_return_if_fail (tag != NULL);
    1810             : 
    1811           0 :         if (tag->grandfathered) {
    1812           0 :                 lt_grandfathered_dump(tag->grandfathered);
    1813           0 :                 return;
    1814             :         }
    1815           0 :         lt_lang_dump(tag->language);
    1816           0 :         if (tag->extlang)
    1817           0 :                 lt_extlang_dump(tag->extlang);
    1818           0 :         if (tag->script)
    1819           0 :                 lt_script_dump(tag->script);
    1820           0 :         if (tag->region)
    1821           0 :                 lt_region_dump(tag->region);
    1822           0 :         for (l = tag->variants; l != NULL; l = lt_list_next(l)) {
    1823           0 :                 lt_variant_t *variant = lt_list_value(l);
    1824             : 
    1825           0 :                 lt_variant_dump(variant);
    1826             :         }
    1827           0 :         if (tag->extension)
    1828           0 :                 lt_extension_dump(tag->extension);
    1829           0 :         if (lt_string_length(tag->privateuse) > 0)
    1830           0 :                 lt_info("Private Use: %s", lt_string_value(tag->privateuse));
    1831             : }
    1832             : 
    1833             : /**
    1834             :  * lt_tag_compare:
    1835             :  * @v1: a #lt_tag_t.
    1836             :  * @v2: a #lt_tag_t.
    1837             :  *
    1838             :  * Compare if @v1 and @v2 is the same object or not.
    1839             :  *
    1840             :  * Returns: %TRUE if it's the same, otherwise %FALSE.
    1841             :  */
    1842             : lt_bool_t
    1843           0 : lt_tag_compare(const lt_tag_t *v1,
    1844             :                const lt_tag_t *v2)
    1845             : {
    1846           0 :         lt_bool_t retval = TRUE;
    1847             :         const lt_list_t *l1, *l2;
    1848             : 
    1849           0 :         lt_return_val_if_fail (v1 != NULL, FALSE);
    1850           0 :         lt_return_val_if_fail (v2 != NULL, FALSE);
    1851           0 :         lt_return_val_if_fail (v1->grandfathered == NULL, FALSE);
    1852           0 :         lt_return_val_if_fail (v2->grandfathered == NULL, FALSE);
    1853             : 
    1854           0 :         retval &= lt_lang_compare(v1->language, v2->language);
    1855           0 :         if (v2->extlang)
    1856           0 :                 retval &= lt_extlang_compare(v1->extlang, v2->extlang);
    1857           0 :         if (v2->script)
    1858           0 :                 retval &= lt_script_compare(v1->script, v2->script);
    1859           0 :         if (v2->region)
    1860           0 :                 retval &= lt_region_compare(v1->region, v2->region);
    1861           0 :         l1 = v1->variants;
    1862           0 :         l2 = v2->variants;
    1863           0 :         while (l2 != NULL) {
    1864             :                 lt_variant_t *vv1, *vv2;
    1865             : 
    1866           0 :                 vv1 = l1 ? lt_list_value(l1) : NULL;
    1867           0 :                 vv2 = l2 ? lt_list_value(l2) : NULL;
    1868           0 :                 retval &= lt_variant_compare(vv1, vv2);
    1869           0 :                 l1 = lt_list_next(l1);
    1870           0 :                 l2 = lt_list_next(l2);
    1871             :         }
    1872           0 :         if (v2->extension)
    1873           0 :                 retval &= lt_extension_compare(v1->extension, v2->extension);
    1874           0 :         if (v2->privateuse && lt_string_length(v2->privateuse) > 0)
    1875           0 :                 retval &= _lt_tag_string_compare(v1->privateuse, v2->privateuse);
    1876             : 
    1877           0 :         return retval;
    1878             : }
    1879             : 
    1880             : /**
    1881             :  * lt_tag_match:
    1882             :  * @v1: a #lt_tag_t.
    1883             :  * @v2: a language range string.
    1884             :  * @error: (allow-none): a #lt_error_t or %NULL.
    1885             :  *
    1886             :  * Try matching of @v1 and @v2. any of subtags in @v2 is allowed to use
    1887             :  * the wildcard according to the syntax in RFC 4647.
    1888             :  *
    1889             :  * Returns: %TRUE if it matches, otherwise %FALSE.
    1890             :  */
    1891             : lt_bool_t
    1892           0 : lt_tag_match(const lt_tag_t  *v1,
    1893             :              const char      *v2,
    1894             :              lt_error_t     **error)
    1895             : {
    1896           0 :         lt_bool_t retval = FALSE;
    1897           0 :         lt_tag_t *t2 = NULL;
    1898           0 :         lt_tag_state_t state = STATE_NONE;
    1899           0 :         lt_error_t *err = NULL;
    1900             : 
    1901           0 :         lt_return_val_if_fail (v1 != NULL, FALSE);
    1902           0 :         lt_return_val_if_fail (v2 != NULL, FALSE);
    1903             : 
    1904           0 :         t2 = lt_tag_new();
    1905           0 :         state = lt_tag_parse_wildcard(t2, v2, &err);
    1906           0 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    1907           0 :                 if (error)
    1908           0 :                         *error = lt_error_ref(err);
    1909             :                 else
    1910           0 :                         lt_error_print(err, LT_ERR_ANY);
    1911           0 :                 lt_error_unref(err);
    1912           0 :                 retval = FALSE;
    1913             :         } else {
    1914           0 :                 retval = _lt_tag_match(v1, t2, state);
    1915             :         }
    1916           0 :         if (t2)
    1917           0 :                 lt_tag_unref(t2);
    1918             : 
    1919           0 :         return retval;
    1920             : }
    1921             : 
    1922             : /**
    1923             :  * lt_tag_lookup:
    1924             :  * @tag: a #lt_tag_t.
    1925             :  * @pattern: a language range string.
    1926             :  * @error: (allow-none): a #lt_error_t or %NULL.
    1927             :  *
    1928             :  * Lookup the language tag that @tag meets with @pattern.
    1929             :  * Any of subtags in @pattern is allowed to use the wildcard according to
    1930             :  * the syntax in RFC 4647.
    1931             :  *
    1932             :  * Returns: a language tag string if any matches, otherwise %NULL.
    1933             :  */
    1934             : char *
    1935           0 : lt_tag_lookup(const lt_tag_t  *tag,
    1936             :               const char      *pattern,
    1937             :               lt_error_t     **error)
    1938             : {
    1939           0 :         lt_tag_t *t2 = NULL;
    1940           0 :         lt_tag_state_t state = STATE_NONE;
    1941           0 :         lt_error_t *err = NULL;
    1942             :         lt_list_t *l;
    1943           0 :         char *retval = NULL;
    1944             : 
    1945           0 :         lt_return_val_if_fail (tag != NULL, NULL);
    1946           0 :         lt_return_val_if_fail (pattern != NULL, NULL);
    1947             : 
    1948           0 :         t2 = lt_tag_new();
    1949           0 :         state = lt_tag_parse_wildcard(t2, pattern, &err);
    1950           0 :         if (err)
    1951           0 :                 goto bail;
    1952           0 :         if (_lt_tag_match(tag, t2, state)) {
    1953             :                 int32_t i;
    1954             : 
    1955           0 :                 for (i = 0; i < (STATE_END - 1); i++) {
    1956           0 :                         if (t2->wildcard_map & (1 << i)) {
    1957           0 :                                 switch (i + 1) {
    1958             :                                     case STATE_LANG:
    1959           0 :                                             lt_tag_set_language(t2, lt_lang_ref(tag->language));
    1960           0 :                                             break;
    1961             :                                     case STATE_EXTLANG:
    1962           0 :                                             lt_tag_free_extlang(t2);
    1963           0 :                                             if (tag->extlang) {
    1964           0 :                                                     lt_tag_set_extlang(t2, lt_extlang_ref(tag->extlang));
    1965             :                                             }
    1966           0 :                                             break;
    1967             :                                     case STATE_SCRIPT:
    1968           0 :                                             lt_tag_free_script(t2);
    1969           0 :                                             if (tag->script) {
    1970           0 :                                                     lt_tag_set_script(t2, lt_script_ref(tag->script));
    1971             :                                             }
    1972           0 :                                             break;
    1973             :                                     case STATE_REGION:
    1974           0 :                                             lt_tag_free_region(t2);
    1975           0 :                                             if (tag->region) {
    1976           0 :                                                     lt_tag_set_region(t2, lt_region_ref(tag->region));
    1977             :                                             }
    1978           0 :                                             break;
    1979             :                                     case STATE_VARIANT:
    1980           0 :                                             lt_tag_free_variants(t2);
    1981           0 :                                             l = tag->variants;
    1982           0 :                                             while (l != NULL) {
    1983           0 :                                                     lt_tag_set_variant(t2, lt_variant_ref(lt_list_value(l)));
    1984           0 :                                                     l = lt_list_next(l);
    1985             :                                             }
    1986           0 :                                             break;
    1987             :                                     case STATE_EXTENSION:
    1988             :                                     case STATE_EXTENSIONTOKEN:
    1989             :                                     case STATE_EXTENSIONTOKEN2:
    1990           0 :                                             lt_tag_free_extension(t2);
    1991           0 :                                             if (tag->extension) {
    1992           0 :                                                     lt_tag_set_extension(t2, lt_extension_ref(tag->extension));
    1993             :                                             }
    1994           0 :                                             break;
    1995             :                                     case STATE_PRIVATEUSE:
    1996             :                                     case STATE_PRIVATEUSETOKEN:
    1997             :                                     case STATE_PRIVATEUSETOKEN2:
    1998           0 :                                             if (t2->privateuse) {
    1999           0 :                                                     lt_string_clear(t2->privateuse);
    2000             :                                             }
    2001           0 :                                             if (tag->privateuse) {
    2002           0 :                                                     lt_string_append(t2->privateuse,
    2003           0 :                                                                      lt_string_value(tag->privateuse));
    2004             :                                             }
    2005           0 :                                             break;
    2006             :                                     default:
    2007           0 :                                             break;
    2008             :                                 }
    2009             :                         }
    2010             :                 }
    2011           0 :                 lt_tag_free_tag_string(t2);
    2012           0 :                 retval = strdup(lt_tag_get_string(t2));
    2013             :         }
    2014             :   bail:
    2015           0 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    2016           0 :                 if (error)
    2017           0 :                         *error = lt_error_ref(err);
    2018             :                 else
    2019           0 :                         lt_error_print(err, LT_ERR_ANY);
    2020           0 :                 lt_error_unref(err);
    2021             :         }
    2022           0 :         if (t2)
    2023           0 :                 lt_tag_unref(t2);
    2024             : 
    2025           0 :         return retval;
    2026             : }
    2027             : 
    2028             : /**
    2029             :  * lt_tag_transform:
    2030             :  * @tag: a #lt_tag_t.
    2031             :  * @error: (allow-none): a #lt_error_t or %NULL.
    2032             :  *
    2033             :  * Transform @tag according to the likelySubtags database provided by CLDR.
    2034             :  *
    2035             :  * Returns: a string.
    2036             :  */
    2037             : char *
    2038           0 : lt_tag_transform(lt_tag_t    *tag,
    2039             :                  lt_error_t **error)
    2040             : {
    2041           0 :         lt_xml_t *xml = NULL;
    2042             :         const char *tag_string;
    2043           0 :         char *retval = NULL;
    2044           0 :         lt_error_t *err = NULL;
    2045             : 
    2046           0 :         lt_return_val_if_fail (tag != NULL, NULL);
    2047             : 
    2048           0 :         tag_string = lt_tag_get_string(tag);
    2049           0 :         if (tag_string) {
    2050             :                 xmlDocPtr doc;
    2051           0 :                 xmlXPathContextPtr xctxt = NULL;
    2052           0 :                 xmlXPathObjectPtr xobj = NULL;
    2053             :                 xmlNodePtr ent;
    2054             :                 xmlChar *to;
    2055           0 :                 char *xpath_string = NULL;
    2056             :                 int n;
    2057             :                 lt_string_t *s;
    2058             :                 int i;
    2059             :                 size_t len;
    2060             : 
    2061           0 :                 xml = lt_xml_new();
    2062           0 :                 doc = lt_xml_get_cldr(xml, LT_XML_CLDR_SUPPLEMENTAL_LIKELY_SUBTAGS);
    2063           0 :                 xctxt = xmlXPathNewContext(doc);
    2064           0 :                 if (!xctxt) {
    2065           0 :                         lt_error_set(&err, LT_ERR_OOM,
    2066             :                                      "Unable to create an instance of xmlXPathContextPtr.");
    2067           0 :                         goto bail;
    2068             :                 }
    2069           0 :                 xpath_string = lt_strdup_printf("/supplementalData/likelySubtags/likelySubtag[@from = '%s']", tag_string);
    2070           0 :                 xobj = xmlXPathEvalExpression((const xmlChar *)xpath_string, xctxt);
    2071           0 :                 if (!xobj) {
    2072           0 :                         lt_error_set(&err, LT_ERR_FAIL_ON_XML,
    2073             :                                      "No valid elements for %s",
    2074             :                                      doc->name);
    2075           0 :                         goto bail;
    2076             :                 }
    2077           0 :                 n = xmlXPathNodeSetGetLength(xobj->nodesetval);
    2078           0 :                 if (n > 1)
    2079           0 :                         lt_warning("Multiple subtag data to be transformed for %s: %d",
    2080             :                                    tag_string, n);
    2081             : 
    2082           0 :                 ent = xmlXPathNodeSetItem(xobj->nodesetval, 0);
    2083           0 :                 if (!ent) {
    2084           0 :                         lt_error_set(&err, LT_ERR_FAIL_ON_XML,
    2085             :                                      "Unable to obtain the xml node via XPath.");
    2086           0 :                         goto bail;
    2087             :                 }
    2088           0 :                 to = xmlGetProp(ent, (const xmlChar *)"to");
    2089           0 :                 s = lt_string_new((const char *)to);
    2090           0 :                 xmlFree(to);
    2091           0 :                 len = lt_string_length(s);
    2092           0 :                 for (i = 0; i < len; i++) {
    2093           0 :                         if (lt_string_at(s, i) == '_')
    2094           0 :                                 lt_string_replace_c(s, i, '-');
    2095             :                 }
    2096           0 :                 retval = lt_string_free(s, FALSE);
    2097             :           bail:
    2098           0 :                 free(xpath_string);
    2099           0 :                 if (xobj)
    2100           0 :                         xmlXPathFreeObject(xobj);
    2101           0 :                 if (xctxt)
    2102           0 :                         xmlXPathFreeContext(xctxt);
    2103           0 :                 lt_xml_unref(xml);
    2104             :         }
    2105           0 :         if (lt_error_is_set(err, LT_ERR_ANY)) {
    2106           0 :                 if (error)
    2107           0 :                         *error = lt_error_ref(err);
    2108             :                 else
    2109           0 :                         lt_error_print(err, LT_ERR_ANY);
    2110           0 :                 lt_error_unref(err);
    2111             :         }
    2112             : 
    2113           0 :         return retval;
    2114             : }
    2115             : 
    2116             : #define DEFUNC_GET_SUBTAG(__func__,__type__)                    \
    2117             :         const __type__ *                                        \
    2118             :         lt_tag_get_ ##__func__ (const lt_tag_t *tag)            \
    2119             :         {                                                       \
    2120             :                 lt_return_val_if_fail (tag != NULL, NULL);      \
    2121             :                                                                 \
    2122             :                 return tag->__func__;                                \
    2123             :         }
    2124             : 
    2125             : /**
    2126             :  * lt_tag_get_language:
    2127             :  * @tag: a #lt_tag_t.
    2128             :  *
    2129             :  * Obtain a #lt_lang_t instance in @tag.
    2130             :  *
    2131             :  * Returns: (transfer none): a #lt_lang_t.
    2132             :  */
    2133          34 : DEFUNC_GET_SUBTAG (language, lt_lang_t)
    2134             : /**
    2135             :  * lt_tag_get_extlang:
    2136             :  * @tag: a #lt_tag_t.
    2137             :  *
    2138             :  * Obtain a #lt_extlang_t instance in @tag.
    2139             :  *
    2140             :  * Returns: (transfer none): a #lt_extlang_t.
    2141             :  */
    2142           0 : DEFUNC_GET_SUBTAG (extlang, lt_extlang_t)
    2143             : /**
    2144             :  * lt_tag_get_script:
    2145             :  * @tag: a #lt_tag_t.
    2146             :  *
    2147             :  * Obtain a #lt_script_t instance in @tag.
    2148             :  *
    2149             :  * Returns: (transfer none): a #lt_script_t.
    2150             :  */
    2151           8 : DEFUNC_GET_SUBTAG (script, lt_script_t)
    2152             : /**
    2153             :  * lt_tag_get_region:
    2154             :  * @tag: a #lt_tag_t.
    2155             :  *
    2156             :  * Obtain a #lt_region_t instance in @tag.
    2157             :  *
    2158             :  * Returns: (transfer none): a #lt_region_t.
    2159             :  */
    2160          40 : DEFUNC_GET_SUBTAG (region, lt_region_t)
    2161             : /**
    2162             :  * lt_tag_get_variants:
    2163             :  * @tag: a #lt_tag_t.
    2164             :  *
    2165             :  * Obtain a list of #lt_variant_t instance in @tag.
    2166             :  *
    2167             :  * Returns: (transfer none): a #lt_list_t containing #lt_variant_t.
    2168             :  */
    2169           0 : DEFUNC_GET_SUBTAG (variants, lt_list_t)
    2170             : /**
    2171             :  * lt_tag_get_extension:
    2172             :  * @tag: a #lt_tag_t.
    2173             :  *
    2174             :  * Obtain a #lt_extension_t instance in @tag.
    2175             :  *
    2176             :  * Returns: (transfer none): a #lt_extension_t.
    2177             :  */
    2178           0 : DEFUNC_GET_SUBTAG (extension, lt_extension_t)
    2179             : /**
    2180             :  * lt_tag_get_privateuse:
    2181             :  * @tag: a #lt_tag_t.
    2182             :  *
    2183             :  * Obtain a #lt_string_t instance in @tag.
    2184             :  *
    2185             :  * Returns: (transfer none): a #lt_string_t.
    2186             :  */
    2187           0 : DEFUNC_GET_SUBTAG (privateuse, lt_string_t)
    2188             : /**
    2189             :  * lt_tag_get_grandfathered:
    2190             :  * @tag: a #lt_tag_t.
    2191             :  *
    2192             :  * Obtain a #lt_grandfathered_t instance in @tag.
    2193             :  *
    2194             :  * Returns: (transfer none): a #lt_grandfathered_t.
    2195             :  */
    2196           0 : DEFUNC_GET_SUBTAG (grandfathered, lt_grandfathered_t)
    2197             : 
    2198             : #undef DEFUNC_GET_SUBTAG

Generated by: LCOV version 1.10