Bug Summary

File:liblangtag/unxlngi6.pro/misc/build/liblangtag-0.2/liblangtag/lt-tag.c
Location:line 1386, column 49
Description:Access to field 'language' results in a dereference of a null pointer (loaded from variable 'ctag')

Annotated 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 * This library is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation, either
12 * version 3 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22#ifdef HAVE_CONFIG_H1
23#include "config.h"
24#endif
25
26#include <string.h>
27#include "lt-database.h"
28#include "lt-error.h"
29#include "lt-ext-module-private.h"
30#include "lt-extension-private.h"
31#include "lt-mem.h"
32#include "lt-utils.h"
33#include "lt-tag.h"
34#include "lt-tag-private.h"
35
36
37/**
38 * SECTION: lt-tag
39 * @Short_Description: A container class for Language tag
40 * @Title: Container - Tag
41 *
42 * This container class provides an interface to deal with the language tag.
43 */
44typedef struct _lt_tag_scanner_t {
45 lt_mem_t parent;
46 gchar *string;
47 gsize length;
48 gsize position;
49} lt_tag_scanner_t;
50
51struct _lt_tag_t {
52 lt_mem_t parent;
53 gint32 wildcard_map;
54 gchar *tag_string;
55 lt_lang_t *language;
56 lt_extlang_t *extlang;
57 lt_script_t *script;
58 lt_region_t *region;
59 GList *variants;
60 lt_extension_t *extension;
61 GString *privateuse;
62 lt_grandfathered_t *grandfathered;
63};
64
65/*< private >*/
66static void
67_lt_tag_gstring_free(GString *string)
68{
69 g_string_free(string, TRUE(!(0)));
70}
71
72static gboolean
73_lt_tag_gstring_compare(const GString *v1,
74 const GString *v2)
75{
76 gboolean retval = FALSE(0);
77 gchar *s1, *s2;
78
79 if (v1 == v2)
80 return TRUE(!(0));
81
82 s1 = v1 ? lt_strlower(g_strdup(v1->str)) : NULL((void*)0);
83 s2 = v2 ? lt_strlower(g_strdup(v2->str)) : NULL((void*)0);
84
85 if (g_strcmp0(s1, "*") == 0 ||
86 g_strcmp0(s2, "*") == 0) {
87 retval = TRUE(!(0));
88 goto bail;
89 }
90
91 retval = g_strcmp0(s1, s2) == 0;
92 bail:
93 g_free(s1);
94 g_free(s2);
95
96 return retval;
97}
98
99static void
100_lt_tag_variants_list_free(GList *list)
101{
102 GList *l;
103
104 for (l = list; l != NULL((void*)0); l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0))) {
105 lt_variant_unref(l->data);
106 }
107 g_list_free(list);
108}
109
110static lt_tag_scanner_t *
111lt_tag_scanner_new(const gchar *tag)
112{
113 lt_tag_scanner_t *retval = lt_mem_alloc_object(sizeof (lt_tag_scanner_t));
114
115 if (retval) {
116 retval->string = g_strdup(tag);
117 lt_mem_add_ref(&retval->parent, retval->string,
118 (lt_destroy_func_t)g_free);
119 retval->length = strlen(tag);
120 }
121
122 return retval;
123}
124
125static void
126lt_tag_scanner_unref(lt_tag_scanner_t *scanner)
127{
128 if (scanner)
129 lt_mem_unref(&scanner->parent);
130}
131
132static gboolean
133lt_tag_scanner_get_token(lt_tag_scanner_t *scanner,
134 gchar **retval,
135 gsize *length,
136 GError **error)
137{
138 GString *string = NULL((void*)0);
139 gchar c;
140 GError *err = NULL((void*)0);
141
142 g_return_val_if_fail (scanner != NULL, FALSE)do{ if (scanner != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "scanner != NULL"); return (
(0)); }; }while (0)
;
143
144 if (scanner->position >= scanner->length) {
145 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_EOT,
146 "No more tokens in buffer");
147 goto bail;
148 }
149
150 string = g_string_new(NULL((void*)0));
151 while (scanner->position < scanner->length) {
152 c = scanner->string[scanner->position++];
153 if (c == 0) {
154 if (string->len == 0) {
155 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_EOT,
156 "No more tokens in buffer");
157 }
158 scanner->position--;
159 break;
160 }
161 if (c == '*') {
162 if (string->len > 0) {
163 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
164 "Invalid wildcard: positon = %" G_GSIZE_FORMAT"u",
165 scanner->position - 1);
166 break;
167 }
168 } else if (!g_ascii_isalnum(c)((g_ascii_table[(guchar) (c)] & G_ASCII_ALNUM) != 0) && c != '-' && c != 0) {
169 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
170 "Invalid character for tag: '%c'", c);
171 break;
172 }
173 g_string_append_c(string, c)g_string_append_c_inline (string, c);
174
175 if (c == '-' ||
176 c == '*')
177 break;
178 if (scanner->string[scanner->position] == '-' ||
179 scanner->string[scanner->position] == 0)
180 break;
181 }
182 bail:
183 if (err) {
184 if (error)
185 *error = g_error_copy(err);
186 else
187 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
188 g_error_free(err);
189 if (string)
190 g_string_free(string, TRUE(!(0)));
191 *retval = NULL((void*)0);
192 *length = 0;
193
194 return FALSE(0);
195 }
196
197 *length = string->len;
198 *retval = g_string_free(string, FALSE(0));
199
200 return TRUE(!(0));
201}
202
203static gboolean
204lt_tag_scanner_is_eof(lt_tag_scanner_t *scanner)
205{
206 g_return_val_if_fail (scanner != NULL, TRUE)do{ if (scanner != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "scanner != NULL"); return (
(!(0))); }; }while (0)
;
207 g_return_val_if_fail (scanner->position <= scanner->length, TRUE)do{ if (scanner->position <= scanner->length) { } else
{ g_return_if_fail_warning ("LangTag", __PRETTY_FUNCTION__, "scanner->position <= scanner->length"
); return ((!(0))); }; }while (0)
;
208
209 return scanner->string[scanner->position] == 0 ||
210 scanner->position >= scanner->length;
211}
212
213static gint
214_lt_tag_variant_compare(gconstpointer a,
215 gconstpointer b)
216{
217 return (gulong)a - (gulong)b;
218}
219
220#define DEFUNC_TAG_FREE(__func__) \
221 G_INLINE_FUNCstatic __inline __attribute__ ((unused)) void \
222 lt_tag_free_ ##__func__ (lt_tag_t *tag) \
223 { \
224 if (tag->__func__) { \
225 lt_mem_remove_ref(&tag->parent, tag->__func__); \
226 tag->__func__ = NULL((void*)0); \
227 } \
228 }
229
230DEFUNC_TAG_FREE (language)
231DEFUNC_TAG_FREE (extlang)
232DEFUNC_TAG_FREE (script)
233DEFUNC_TAG_FREE (region)
234DEFUNC_TAG_FREE (variants)
235DEFUNC_TAG_FREE (extension)
236DEFUNC_TAG_FREE (grandfathered)
237DEFUNC_TAG_FREE (tag_string)
238
239#undef DEFUNC_TAG_FREE
240
241#define DEFUNC_TAG_SET(__func__, __unref_func__) \
242 G_INLINE_FUNCstatic __inline __attribute__ ((unused)) void \
243 lt_tag_set_ ##__func__ (lt_tag_t *tag, gpointer p) \
244 { \
245 lt_tag_free_ ##__func__ (tag); \
246 if (p) { \
247 tag->__func__ = p; \
248 lt_mem_add_ref(&tag->parent, tag->__func__, \
249 (lt_destroy_func_t)__unref_func__); \
250 } \
251 }
252
253DEFUNC_TAG_SET (language, lt_lang_unref)
254DEFUNC_TAG_SET (extlang, lt_extlang_unref)
255DEFUNC_TAG_SET (script, lt_script_unref)
256DEFUNC_TAG_SET (region, lt_region_unref)
257DEFUNC_TAG_SET (extension, lt_extension_unref)
258DEFUNC_TAG_SET (grandfathered, lt_grandfathered_unref)
259DEFUNC_TAG_SET (tag_string, g_free)
260
261G_INLINE_FUNCstatic __inline __attribute__ ((unused)) void
262lt_tag_set_variant(lt_tag_t *tag,
263 gpointer p)
264{
265 gboolean no_variants = (tag->variants == NULL((void*)0));
266
267 if (p) {
268 tag->variants = g_list_append(tag->variants, p);
269 if (no_variants)
270 lt_mem_add_ref(&tag->parent, tag->variants,
271 (lt_destroy_func_t)_lt_tag_variants_list_free);
272 } else {
273 g_warn_if_reached()do { g_warn_message ("LangTag", "lt-tag.c", 273, ((const char
*) (__PRETTY_FUNCTION__)), ((void*)0)); } while (0)
;
274 }
275}
276
277#undef DEFUNC_TAG_SET
278
279static void
280lt_tag_fill_wildcard(lt_tag_t *tag,
281 lt_tag_state_t begin,
282 lt_tag_state_t end)
283{
284 lt_tag_state_t i;
285 lt_lang_db_t *langdb;
286 lt_extlang_db_t *extlangdb;
287 lt_script_db_t *scriptdb;
288 lt_region_db_t *regiondb;
289 lt_variant_db_t *variantdb;
290 lt_extension_t *e;
291
292 for (i = begin; i < end; i++) {
293 tag->wildcard_map |= (1 << (i - 1));
294 switch (i) {
295 case STATE_LANG:
296 langdb = lt_db_get_lang();
297 lt_tag_set_language(tag, lt_lang_db_lookup(langdb, "*"));
298 lt_lang_db_unref(langdb);
299 break;
300 case STATE_EXTLANG:
301 extlangdb = lt_db_get_extlang();
302 lt_tag_set_extlang(tag, lt_extlang_db_lookup(extlangdb, "*"));
303 lt_extlang_db_unref(extlangdb);
304 break;
305 case STATE_SCRIPT:
306 scriptdb = lt_db_get_script();
307 lt_tag_set_script(tag, lt_script_db_lookup(scriptdb, "*"));
308 lt_script_db_unref(scriptdb);
309 break;
310 case STATE_REGION:
311 regiondb = lt_db_get_region();
312 lt_tag_set_region(tag, lt_region_db_lookup(regiondb, "*"));
313 lt_region_db_unref(regiondb);
314 break;
315 case STATE_VARIANT:
316 variantdb = lt_db_get_variant();
317 lt_tag_set_variant(tag, lt_variant_db_lookup(variantdb, "*"));
318 lt_variant_db_unref(variantdb);
319 break;
320 case STATE_EXTENSION:
321 e = lt_extension_create();
322 lt_extension_add_singleton(e, '*', NULL((void*)0), NULL((void*)0));
323 lt_tag_set_extension(tag, e);
324 break;
325 case STATE_PRIVATEUSE:
326 g_string_truncate(tag->privateuse, 0);
327 g_string_append(tag->privateuse, "*");
328 break;
329 default:
330 break;
331 }
332 }
333}
334
335static gboolean
336lt_tag_parse_prestate(lt_tag_t *tag,
337 const gchar *token,
338 gsize length,
339 lt_tag_state_t *state,
340 GError **error)
341{
342 gboolean retval = TRUE(!(0));
343
344 if (g_strcmp0(token, "-") == 0) {
345 switch (*state) {
346 case STATE_PRE_EXTLANG:
347 *state = STATE_EXTLANG;
348 break;
349 case STATE_PRE_SCRIPT:
350 *state = STATE_SCRIPT;
351 break;
352 case STATE_PRE_REGION:
353 *state = STATE_REGION;
354 break;
355 case STATE_PRE_VARIANT:
356 *state = STATE_VARIANT;
357 break;
358 case STATE_PRE_EXTENSION:
359 *state = STATE_EXTENSION;
360 break;
361 case STATE_IN_EXTENSION:
362 *state = STATE_EXTENSIONTOKEN;
363 break;
364 case STATE_IN_EXTENSIONTOKEN:
365 *state = STATE_EXTENSIONTOKEN2;
366 break;
367 case STATE_PRE_PRIVATEUSE:
368 *state = STATE_PRIVATEUSE;
369 break;
370 case STATE_IN_PRIVATEUSE:
371 *state = STATE_PRIVATEUSETOKEN;
372 break;
373 case STATE_IN_PRIVATEUSETOKEN:
374 *state = STATE_PRIVATEUSETOKEN2;
375 break;
376 default:
377 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
378 "Invalid syntax found during parsing a token: %s",
379 token);
380 retval = FALSE(0);
381 break;
382 }
383 } else {
384 retval = FALSE(0);
385 }
386
387 return retval;
388}
389
390static gboolean
391lt_tag_parse_state(lt_tag_t *tag,
392 const gchar *token,
393 gsize length,
394 lt_tag_state_t *state,
395 GError **error)
396{
397 gboolean retval = TRUE(!(0));
398 const gchar *p;
399
400 switch (*state) {
401 case STATE_LANG:
402 if (length == 1) {
403 if (g_ascii_strcasecmp(token, "x") == 0) {
404 g_string_append(tag->privateuse, token);
405 *state = STATE_IN_PRIVATEUSE;
406 break;
407 } else {
408 invalid_tag:
409 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
410 "Invalid language subtag: %s", token);
411 break;
412 }
413 } else if (length >= 2 && length <= 3) {
414 lt_lang_db_t *langdb = lt_db_get_lang();
415
416 /* shortest ISO 639 code */
417 tag->language = lt_lang_db_lookup(langdb, token);
418 lt_lang_db_unref(langdb);
419 if (!tag->language) {
420 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
421 "Unknown ISO 639 code: %s",
422 token);
423 break;
424 }
425 /* validate if it's really shortest one */
426 p = lt_lang_get_tag(tag->language);
427 if (!p || g_ascii_strcasecmp(token, p) != 0) {
428 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
429 "No such language subtag: %s",
430 token);
431 lt_lang_unref(tag->language);
432 tag->language = NULL((void*)0);
433 break;
434 }
435 lt_mem_add_ref(&tag->parent, tag->language,
436 (lt_destroy_func_t)lt_lang_unref);
437 *state = STATE_PRE_EXTLANG;
438 } else if (length == 4) {
439 /* reserved for future use */
440 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
441 "Reserved for future use: %s",
442 token);
443 } else if (length >= 5 && length <= 8) {
444 /* registered language subtag */
445 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
446 "XXX: registered language tag: %s",
447 token);
448 } else {
449 goto invalid_tag;
450 }
451 break;
452 case STATE_EXTLANG:
453 if (length == 3) {
454 lt_extlang_db_t *extlangdb = lt_db_get_extlang();
455
456 tag->extlang = lt_extlang_db_lookup(extlangdb, token);
457 lt_extlang_db_unref(extlangdb);
458 if (tag->extlang) {
459 const gchar *prefix = lt_extlang_get_prefix(tag->extlang);
460 const gchar *subtag = lt_extlang_get_tag(tag->extlang);
461 const gchar *lang = lt_lang_get_better_tag(tag->language);
462
463 if (prefix &&
464 g_ascii_strcasecmp(prefix, lang) != 0) {
465 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
466 "extlang '%s' is supposed to be used with %s, but %s",
467 subtag, prefix, lang);
468 lt_extlang_unref(tag->extlang);
469 tag->extlang = NULL((void*)0);
470 } else {
471 lt_mem_add_ref(&tag->parent, tag->extlang,
472 (lt_destroy_func_t)lt_extlang_unref);
473 *state = STATE_PRE_SCRIPT;
474 }
475 break;
476 }
477 /* try to check something else */
478 } else {
479 /* it may be a script */
480 }
481 case STATE_SCRIPT:
482 if (length == 4) {
483 lt_script_db_t *scriptdb = lt_db_get_script();
484
485 lt_tag_set_script(tag, lt_script_db_lookup(scriptdb, token));
486 lt_script_db_unref(scriptdb);
487 if (tag->script) {
488 *state = STATE_PRE_REGION;
489 break;
490 }
491 /* try to check something else */
492 } else {
493 /* it may be a region */
494 }
495 case STATE_REGION:
496 if (length == 2 ||
497 (length == 3 &&
498 g_ascii_isdigit(token[0])((g_ascii_table[(guchar) (token[0])] & G_ASCII_DIGIT) != 0
)
&&
499 g_ascii_isdigit(token[1])((g_ascii_table[(guchar) (token[1])] & G_ASCII_DIGIT) != 0
)
&&
500 g_ascii_isdigit(token[2])((g_ascii_table[(guchar) (token[2])] & G_ASCII_DIGIT) != 0
)
)) {
501 lt_region_db_t *regiondb = lt_db_get_region();
502
503 lt_tag_set_region(tag, lt_region_db_lookup(regiondb, token));
504 lt_region_db_unref(regiondb);
505 if (tag->region) {
506 *state = STATE_PRE_VARIANT;
507 break;
508 }
509 /* try to check something else */
510 } else {
511 /* it may be a variant */
512 }
513 case STATE_VARIANT:
514 if ((length >=5 && length <= 8) ||
515 (length == 4 && g_ascii_isdigit(token[0])((g_ascii_table[(guchar) (token[0])] & G_ASCII_DIGIT) != 0
)
)) {
516 lt_variant_db_t *variantdb = lt_db_get_variant();
517 lt_variant_t *variant;
518
519 variant = lt_variant_db_lookup(variantdb, token);
520 lt_variant_db_unref(variantdb);
521 if (variant) {
522 const GList *prefixes = lt_variant_get_prefix(variant), *l;
523 gchar *langtag = lt_tag_canonicalize(tag, error);
524 GString *str_prefixes = g_string_new(NULL((void*)0));
525 gboolean matched = FALSE(0);
526
527 if (error && *error) {
528 /* ignore it and fallback to the original tag string */
529 g_error_free(*error);
530 *error = NULL((void*)0);
531 langtag = g_strdup(tag->tag_string);
532 }
533 for (l = prefixes; l != NULL((void*)0); l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0))) {
534 const gchar *s = l->data;
535
536 if (str_prefixes->len > 0)
537 g_string_append(str_prefixes, ",");
538 g_string_append(str_prefixes, s);
539
540 if (g_ascii_strncasecmp(s, langtag, strlen(s)) == 0) {
541 matched = TRUE(!(0));
542 break;
543 }
544 }
545 if (prefixes && !matched) {
546 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
547 "variant '%s' is supposed to be used with %s, but %s",
548 token, str_prefixes->str, tag->tag_string);
549 lt_variant_unref(variant);
550 } else {
551 if (!tag->variants) {
552 lt_tag_set_variant(tag, variant);
553 } else {
554 GList *prefixes = (GList *)lt_variant_get_prefix(variant);
555 const gchar *tstr;
556
557 lt_tag_free_tag_string(tag);
558 tstr = lt_tag_get_string(tag);
559 if (prefixes && !g_list_find_custom(prefixes, tstr, (GCompareFunc)g_strcmp0)) {
560 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
561 "Variant isn't allowed for %s: %s",
562 tstr,
563 lt_variant_get_tag(variant));
564 lt_variant_unref(variant);
565 } else if (!prefixes && g_list_find_custom(tag->variants, variant, _lt_tag_variant_compare)) {
566 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
567 "Duplicate variants: %s",
568 lt_variant_get_tag(variant));
569 lt_variant_unref(variant);
570 } else {
571 tag->variants = g_list_append(tag->variants,
572 variant);
573 }
574 }
575 /* multiple variants are allowed. */
576 *state = STATE_PRE_VARIANT;
577 }
578 g_free(langtag);
579 g_string_free(str_prefixes, TRUE(!(0)));
580 break;
581 }
582 /* try to check something else */
583 } else {
584 /* it may be an extension */
585 }
586 case STATE_EXTENSION:
587 extension:
588 if (length == 1 &&
589 token[0] != 'x' &&
590 token[0] != 'X' &&
591 token[0] != '*' &&
592 token[0] != '-') {
593 if (!tag->extension)
594 lt_tag_set_extension(tag, lt_extension_create());
595 if (lt_extension_has_singleton(tag->extension, token[0])) {
596 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
597 "Duplicate singleton for extension: %s", token);
598 } else {
599 if (lt_extension_add_singleton(tag->extension,
600 token[0],
601 tag, error)) {
602 *state = STATE_IN_EXTENSION;
603 }
604 }
605 break;
606 } else {
607 /* it may be a private use */
608 }
609 case STATE_PRIVATEUSE:
610 if (length == 1 && (token[0] == 'x' || token[0] == 'X')) {
611 g_string_append(tag->privateuse, token);
612 *state = STATE_IN_PRIVATEUSE;
613 } else {
614 /* No state to try */
615 retval = FALSE(0);
616 }
617 break;
618 case STATE_EXTENSIONTOKEN:
619 case STATE_EXTENSIONTOKEN2:
620 if (length >= 2 && length <= 8) {
621 if (lt_extension_add_tag(tag->extension,
622 token, error))
623 *state = STATE_IN_EXTENSIONTOKEN;
624 } else {
625 if (*state == STATE_EXTENSIONTOKEN2 &&
626 lt_extension_validate_state(tag->extension)) {
627 /* No need to destroy the previous tokens.
628 * fallback to check the extension again.
629 */
630 goto extension;
631 }
632 lt_extension_cancel_tag(tag->extension);
633 /* No state to try */
634 retval = FALSE(0);
635 }
636 break;
637 case STATE_PRIVATEUSETOKEN:
638 case STATE_PRIVATEUSETOKEN2:
639 if (length <= 8) {
640 g_string_append_printf(tag->privateuse, "-%s", token);
641 *state = STATE_IN_PRIVATEUSETOKEN;
642 } else {
643 /* 'x'/'X' is reserved singleton for the private use subtag.
644 * so nothing to fallback to anything else.
645 */
646 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
647 "Invalid tag for the private use: token = '%s'",
648 token);
649 }
650 break;
651 default:
652 g_set_error(error, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
653 "Unable to parse tag: %s, token = '%s' state = %d",
654 tag->tag_string, token, *state);
655 break;
656 }
657 if (*error)
658 retval = FALSE(0);
659
660 return retval;
661}
662
663static gboolean
664_lt_tag_parse(lt_tag_t *tag,
665 const gchar *langtag,
666 gboolean allow_wildcard,
667 lt_tag_state_t *state,
668 GError **error)
669{
670 lt_tag_scanner_t *scanner = NULL((void*)0);
671 lt_grandfathered_db_t *grandfathereddb;
672 gchar *token = NULL((void*)0);
673 gsize len = 0;
674 GError *err = NULL((void*)0);
675 gboolean retval = TRUE(!(0));
676 lt_tag_state_t wildcard = STATE_NONE;
677 gint count = 0;
678
679 g_return_val_if_fail (tag != NULL, FALSE)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return ((0)
); }; }while (0)
;
680 g_return_val_if_fail (langtag != NULL, FALSE)do{ if (langtag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "langtag != NULL"); return (
(0)); }; }while (0)
;
681
682 lt_tag_clear(tag);
683
684 grandfathereddb = lt_db_get_grandfathered();
685 lt_tag_set_grandfathered(tag, lt_grandfathered_db_lookup(grandfathereddb, langtag));
686 lt_grandfathered_db_unref(grandfathereddb);
687 if (tag->grandfathered) {
688 /* no need to lookup anymore. */
689 goto bail;
690 }
691
692 scanner = lt_tag_scanner_new(langtag);
693 *state = STATE_LANG;
694 while (!lt_tag_scanner_is_eof(scanner)) {
695 if (token) {
696 g_free(token);
697 token = NULL((void*)0);
698 }
699 if (!lt_tag_scanner_get_token(scanner, &token, &len, &err)) {
700 if (err)
701 break;
702 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
703 "Unrecoverable error");
704 break;
705 }
706 count++;
707 if (!token || len == 0) {
708 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
709 "No valid tokens found");
710 break;
711 }
712 if (!lt_tag_parse_prestate(tag, token, len, state, &err)) {
713 if (err)
714 break;
715 if (allow_wildcard && g_strcmp0(token, "*") == 0) {
716 wildcard = *state;
717 if (*state == STATE_LANG)
718 *state += 1;
719 else
720 *state -= 1;
721 } else {
722 if (!lt_tag_parse_state(tag, token, len, state, &err))
723 break;
724 if (wildcard != STATE_NONE) {
725 lt_tag_fill_wildcard(tag, wildcard, *state - 1);
726 wildcard = STATE_NONE;
727 }
728 }
729 }
730 }
731 if (wildcard != STATE_NONE) {
732 lt_tag_fill_wildcard(tag, wildcard, STATE_END);
733 }
734 if (!err &&
735 *state != STATE_PRE_EXTLANG &&
736 *state != STATE_PRE_SCRIPT &&
737 *state != STATE_PRE_REGION &&
738 *state != STATE_PRE_VARIANT &&
739 *state != STATE_PRE_EXTENSION &&
740 *state != STATE_PRE_PRIVATEUSE &&
741 *state != STATE_IN_EXTENSIONTOKEN &&
742 *state != STATE_IN_PRIVATEUSETOKEN &&
743 *state != STATE_NONE) {
744 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
745 "Invalid tag: %s, last token = '%s', state = %d, parsed count = %d",
746 langtag, token, *state, count);
747 }
748 bail:
749 lt_tag_set_tag_string(tag, g_strdup(langtag));
750 lt_tag_scanner_unref(scanner);
751 if (err) {
752 if (error)
753 *error = g_error_copy(err);
754 else
755 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
756 g_error_free(err);
757 retval = FALSE(0);
758 }
759 g_free(token);
760
761 return retval;
762}
763
764static gboolean
765_lt_tag_match(const lt_tag_t *v1,
766 lt_tag_t *v2,
767 lt_tag_state_t state)
768{
769 g_return_val_if_fail (v1 != NULL, FALSE)do{ if (v1 != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v1 != NULL"); return ((0))
; }; }while (0)
;
770 g_return_val_if_fail (v2 != NULL, FALSE)do{ if (v2 != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v2 != NULL"); return ((0))
; }; }while (0)
;
771
772 if (state > STATE_EXTLANG && !v2->extlang && v1->extlang) {
773 lt_extlang_db_t *db = lt_db_get_extlang();
774
775 lt_tag_set_extlang(v2, lt_extlang_db_lookup(db, ""));
776 lt_extlang_db_unref(db);
777 }
778 if (state > STATE_SCRIPT && !v2->script && v1->script) {
779 lt_script_db_t *db = lt_db_get_script();
780
781 lt_tag_set_script(v2, lt_script_db_lookup(db, ""));
782 lt_script_db_unref(db);
783 }
784 if (state > STATE_REGION && !v2->region && v1->region) {
785 lt_region_db_t *db = lt_db_get_region();
786
787 lt_tag_set_region(v2, lt_region_db_lookup(db, ""));
788 lt_region_db_unref(db);
789 }
790 if (state > STATE_VARIANT && !v2->variants && v1->variants) {
791 lt_variant_db_t *db = lt_db_get_variant();
792
793 lt_tag_set_variant(v2, lt_variant_db_lookup(db, ""));
794 lt_variant_db_unref(db);
795 }
796 if (state > STATE_EXTENSION && !v2->extension && v1->extension) {
797 lt_extension_t *e = lt_extension_create();
798
799 lt_extension_add_singleton(e, ' ', NULL((void*)0), NULL((void*)0));
800 lt_tag_set_extension(v2, e);
801 }
802
803 return lt_tag_compare(v1, v2);
804}
805
806static void
807_lt_tag_subtract(lt_tag_t *tag,
808 const lt_tag_t *rtag)
809{
810 if (rtag->language) {
811 lt_tag_free_language(tag);
812 }
813 if (rtag->extlang) {
814 lt_tag_free_extlang(tag);
815 }
816 if (rtag->script) {
817 lt_tag_free_script(tag);
818 }
819 if (rtag->region) {
820 lt_tag_free_region(tag);
821 }
822 if (rtag->variants) {
823 lt_tag_free_variants(tag);
824 }
825 if (rtag->extension) {
826 /* XXX: how to deal with the multiple extensions? */
827 lt_tag_free_extension(tag);
828 }
829 if (rtag->privateuse) {
830 if (tag->privateuse)
831 g_string_truncate(tag->privateuse, 0);
832 }
833}
834
835static void
836_lt_tag_replace(lt_tag_t *tag,
837 const lt_tag_t *rtag)
838{
839 if (rtag->language) {
840 g_return_if_fail (!tag->language)do{ if (!tag->language) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "!tag->language"); return
; }; }while (0)
;
841 lt_tag_set_language(tag, lt_lang_ref(rtag->language));
842 }
843 if (rtag->extlang) {
844 g_return_if_fail (!tag->extlang)do{ if (!tag->extlang) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "!tag->extlang"); return
; }; }while (0)
;
845 lt_tag_set_extlang(tag, lt_extlang_ref(rtag->extlang));
846 }
847 if (rtag->script) {
848 g_return_if_fail (!tag->script)do{ if (!tag->script) { } else { g_return_if_fail_warning (
"LangTag", __PRETTY_FUNCTION__, "!tag->script"); return; }
; }while (0)
;
849 lt_tag_set_script(tag, lt_script_ref(rtag->script));
850 }
851 if (rtag->region) {
852 g_return_if_fail (!tag->region)do{ if (!tag->region) { } else { g_return_if_fail_warning (
"LangTag", __PRETTY_FUNCTION__, "!tag->region"); return; }
; }while (0)
;
853 lt_tag_set_region(tag, lt_region_ref(rtag->region));
854 }
855 if (rtag->variants) {
856 GList *l = rtag->variants;
857
858 g_return_if_fail (!tag->variants)do{ if (!tag->variants) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "!tag->variants"); return
; }; }while (0)
;
859
860 while (l != NULL((void*)0)) {
861 lt_tag_set_variant(tag, lt_variant_ref(l->data));
862 l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0));
863 }
864 }
865 if (rtag->extension) {
866 g_return_if_fail (!tag->extension)do{ if (!tag->extension) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "!tag->extension"); return
; }; }while (0)
;
867 lt_tag_set_extension(tag, lt_extension_ref(rtag->extension));
868 }
869 if (rtag->privateuse) {
870 g_string_truncate(tag->privateuse, 0);
871 g_string_append(tag->privateuse, rtag->privateuse->str);
872 }
873}
874
875/*< protected >*/
876lt_tag_state_t
877lt_tag_parse_wildcard(lt_tag_t *tag,
878 const gchar *tag_string,
879 GError **error)
880{
881 GError *err = NULL((void*)0);
882 lt_tag_state_t retval = STATE_NONE;
883 gboolean ret = _lt_tag_parse(tag, tag_string, TRUE(!(0)), &retval, &err);
884
885 if (!ret && !err) {
886 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_FAIL_ON_SCANNER,
887 "Unknown error during parsing a tag.");
888 }
889 if (err) {
890 if (error)
891 *error = g_error_copy(err);
892 else
893 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
894 g_error_free(err);
895 }
896
897 return retval;
898}
899
900/*< public >*/
901/**
902 * lt_tag_new:
903 *
904 * Create a new instance of #lt_tag_t.
905 *
906 * Returns: (transfer full): a new instance of #lt_tag_t.
907 */
908lt_tag_t *
909lt_tag_new(void)
910{
911 lt_tag_t *retval = lt_mem_alloc_object(sizeof (lt_tag_t));
912
913 if (retval) {
914 retval->privateuse = g_string_new(NULL((void*)0));
915 lt_mem_add_ref(&retval->parent, retval->privateuse,
916 (lt_destroy_func_t)_lt_tag_gstring_free);
917 }
918
919 return retval;
920}
921
922/**
923 * lt_tag_ref:
924 * @tag: a #lt_tag_t.
925 *
926 * Increases the reference count of @tag.
927 *
928 * Returns: (transfer none): the same @tag object.
929 */
930lt_tag_t *
931lt_tag_ref(lt_tag_t *tag)
932{
933 g_return_val_if_fail (tag != NULL, NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return (((void
*)0)); }; }while (0)
;
934
935 return lt_mem_ref(&tag->parent);
936}
937
938/**
939 * lt_tag_unref:
940 * @tag: a #lt_tag_t.
941 *
942 * Decreases the reference count of @tag. when its reference count
943 * drops to 0, the object is finalized (i.e. its memory is freed).
944 */
945void
946lt_tag_unref(lt_tag_t *tag)
947{
948 if (tag)
949 lt_mem_unref(&tag->parent);
950}
951
952/**
953 * lt_tag_clear:
954 * @tag: a #lt_tag_t.
955 *
956 * (Re-)Initialize all of the subtag information stored in @tag.
957 */
958void
959lt_tag_clear(lt_tag_t *tag)
960{
961 g_return_if_fail (tag != NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return; }; }
while (0)
;
962
963 lt_tag_free_tag_string(tag);
964 lt_tag_free_language(tag);
965 lt_tag_free_extlang(tag);
966 lt_tag_free_script(tag);
967 lt_tag_free_region(tag);
968 lt_tag_free_variants(tag);
969 lt_tag_free_extension(tag);
970 if (tag->privateuse) {
971 g_string_truncate(tag->privateuse, 0);
972 }
973 lt_tag_free_grandfathered(tag);
974}
975
976/**
977 * lt_tag_parse:
978 * @tag: a #lt_tag_t.
979 * @tag_string: language tag to be parsed.
980 * @error: (allow-none): a #GError or %NULL.
981 *
982 * Parse @tag_string and create appropriate instances for subtags.
983 *
984 * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
985 */
986gboolean
987lt_tag_parse(lt_tag_t *tag,
988 const gchar *tag_string,
989 GError **error)
990{
991 lt_tag_state_t state = STATE_NONE;
992
993 return _lt_tag_parse(tag, tag_string, FALSE(0), &state, error);
994}
995
996/**
997 * lt_tag_copy:
998 * @tag: a #lt_tag_t.
999 *
1000 * Create a copy instance of @tag.
1001 *
1002 * Returns: (transfer full): a new instance of #lt_tag_t or %NULL if fails.
1003 */
1004lt_tag_t *
1005lt_tag_copy(const lt_tag_t *tag)
1006{
1007 lt_tag_t *retval;
1008 GList *l;
1009
1010 g_return_val_if_fail (tag != NULL, NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return (((void
*)0)); }; }while (0)
;
1011
1012 retval = lt_tag_new();
1013 retval->wildcard_map = tag->wildcard_map;
1014 if (tag->language) {
1015 lt_tag_set_language(retval, lt_lang_ref(tag->language));
1016 }
1017 if (tag->extlang) {
1018 lt_tag_set_extlang(retval, lt_extlang_ref(tag->extlang));
1019 }
1020 if (tag->script) {
1021 lt_tag_set_script(retval, lt_script_ref(tag->script));
1022 }
1023 if (tag->region) {
1024 lt_tag_set_region(retval, lt_region_ref(tag->region));
1025 }
1026 l = tag->variants;
1027 while (l != NULL((void*)0)) {
1028 lt_tag_set_variant(retval, lt_variant_ref(l->data));
1029 l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0));
1030 }
1031 if (tag->extension) {
1032 lt_tag_set_extension(retval, lt_extension_copy(tag->extension));
1033 }
1034 if (tag->privateuse) {
1035 g_string_append(retval->privateuse, tag->privateuse->str);
1036 }
1037 if (tag->grandfathered) {
1038 lt_tag_set_grandfathered(retval, lt_grandfathered_ref(tag->grandfathered));
1039 }
1040
1041 return retval;
1042}
1043
1044/**
1045 * lt_tag_truncate:
1046 * @tag: a #lt_tag_t.
1047 * @error: (allow-none): a #GError.
1048 *
1049 * Truncate the last subtag.
1050 *
1051 * Returns: %TRUE if a subtag was truncated, otherwise %FALSE.
1052 */
1053gboolean
1054lt_tag_truncate(lt_tag_t *tag,
1055 GError **error)
1056{
1057 GError *err = NULL((void*)0);
1058 gboolean retval = TRUE(!(0));
1059
1060 g_return_val_if_fail (tag != NULL, FALSE)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return ((0)
); }; }while (0)
;
1061
1062 if (tag->grandfathered) {
1063 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_NO_TAG,
1064 "Grandfathered subtag can't be truncated.");
1065 goto bail;
1066 }
1067 while (1) {
1068 if (tag->privateuse && tag->privateuse->len > 0) {
1069 g_string_truncate(tag->privateuse, 0);
1070 break;
1071 }
1072 if (tag->extension) {
1073 gint i;
1074 gchar c;
1075 gboolean has_tag = FALSE(0);
1076
1077 lt_extension_truncate(tag->extension);
1078 for (i = 0; i < LT_MAX_EXT_MODULES(('9' - '0' + 1) + ('z' - 'a' + 1) + 2); i++) {
1079 c = lt_ext_module_singleton_int_to_char(i);
1080
1081 if (c == 'x')
1082 continue;
1083 has_tag = lt_extension_has_singleton(tag->extension, c);
1084 if (has_tag)
1085 break;
1086 }
1087 if (!has_tag) {
1088 lt_tag_free_extension(tag);
1089 }
1090 break;
1091 }
1092 if (tag->variants) {
1093 GList *l = g_list_last(tag->variants);
1094 lt_variant_t *v = l->data;
1095
1096 if (tag->variants == l) {
1097 lt_mem_delete_ref(&tag->parent, tag->variants);
1098 tag->variants = g_list_delete_link(tag->variants, l);
1099 if (tag->variants)
1100 lt_mem_add_ref(&tag->parent, tag->variants,
1101 (lt_destroy_func_t)_lt_tag_variants_list_free);
1102 } else {
1103 l = g_list_delete_link(l, l);
1104 }
1105 lt_variant_unref(v);
1106 break;
1107 }
1108 if (tag->region) {
1109 lt_tag_free_region(tag);
1110 break;
1111 }
1112 if (tag->script) {
1113 lt_tag_free_script(tag);
1114 break;
1115 }
1116 if (tag->extlang) {
1117 lt_tag_free_extlang(tag);
1118 break;
1119 }
1120 if (tag->language) {
1121 lt_tag_free_language(tag);
1122 break;
1123 }
1124 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_NO_TAG,
1125 "No tags to be truncated.");
1126 goto bail;
1127 }
1128 lt_tag_free_tag_string(tag);
1129 bail:
1130 if (err) {
1131 if (error)
1132 *error = g_error_copy(err);
1133 else
1134 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
1135 g_error_free(err);
1136 retval = FALSE(0);
1137 }
1138
1139 return retval;
1140}
1141
1142/**
1143 * lt_tag_get_string:
1144 * @tag: a #lt_tag_t.
1145 *
1146 * Obtains a language tag in string.
1147 *
1148 * Returns: a language tag string.
1149 */
1150const gchar *
1151lt_tag_get_string(lt_tag_t *tag)
1152{
1153 GString *string;
1154 GList *l;
1155
1156 if (tag->tag_string)
1157 return tag->tag_string;
1158
1159 string = g_string_new(NULL((void*)0));
1160 if (tag->grandfathered)
1161 g_string_append(string, lt_grandfathered_get_tag(tag->grandfathered));
1162 else if (tag->language) {
1163 g_string_append(string, lt_lang_get_tag(tag->language));
1164 if (tag->extlang)
1165 g_string_append_printf(string, "-%s",
1166 lt_extlang_get_tag(tag->extlang));
1167 if (tag->script)
1168 g_string_append_printf(string, "-%s",
1169 lt_script_get_tag(tag->script));
1170 if (tag->region)
1171 g_string_append_printf(string, "-%s",
1172 lt_region_get_tag(tag->region));
1173 l = tag->variants;
1174 while (l != NULL((void*)0)) {
1175 g_string_append_printf(string, "-%s",
1176 lt_variant_get_tag(l->data));
1177 l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0));
1178 }
1179 if (tag->extension)
1180 g_string_append_printf(string, "-%s",
1181 lt_extension_get_tag(tag->extension));
1182 if (tag->privateuse && tag->privateuse->len > 0)
1183 g_string_append_printf(string, "-%s", tag->privateuse->str);
1184 } else if (tag->privateuse && tag->privateuse->len > 0) {
1185 g_string_append(string, tag->privateuse->str);
1186 }
1187
1188 lt_tag_set_tag_string(tag, g_string_free(string, FALSE(0)));
1189
1190 return tag->tag_string;
1191}
1192
1193/**
1194 * lt_tag_canonicalize:
1195 * @tag: a #lt_tag_t.
1196 * @error: (allow-none): a #GError or %NULL.
1197 *
1198 * Canonicalize the language tag according to various information of subtags.
1199 *
1200 * Returns: a language tag string.
1201 */
1202gchar *
1203lt_tag_canonicalize(lt_tag_t *tag,
1204 GError **error)
1205{
1206 gchar *retval = NULL((void*)0);
1207 GString *string = NULL((void*)0);
1208 GError *err = NULL((void*)0);
1209 GList *l;
1210 lt_redundant_db_t *rdb = NULL((void*)0);
1211 lt_redundant_t *r = NULL((void*)0);
1212 lt_tag_t *ctag = NULL((void*)0);
1213
1214 g_return_val_if_fail (tag != NULL, NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return (((void
*)0)); }; }while (0)
;
1215
1216 string = g_string_new(NULL((void*)0));
1217 if (tag->grandfathered) {
1218 g_string_append(string, lt_grandfathered_get_better_tag(tag->grandfathered));
1219 goto bail1;
1220 }
1221
1222 ctag = lt_tag_copy(tag);
1223 rdb = lt_db_get_redundant();
1224 while (1) {
1225 const gchar *tag_string = lt_tag_get_string(ctag);
1226
1227 if (tag_string[0] == 0)
1228 break;
1229 if (r)
1230 lt_redundant_unref(r);
1231 r = lt_redundant_db_lookup(rdb, tag_string);
1232 if (r) {
1233 const gchar *preferred = lt_redundant_get_preferred_tag(r);
1234
1235 if (preferred) {
1236 lt_tag_t *rtag = lt_tag_new();
1237 lt_tag_t *ntag = lt_tag_new();
1238
1239 if (!lt_tag_parse(rtag, lt_redundant_get_tag(r), &err)) {
1240 lt_tag_unref(rtag);
1241 lt_tag_unref(ntag);
1242 goto bail1;
1243 }
1244 if (!lt_tag_parse(ntag, preferred, &err)) {
1245 lt_tag_unref(rtag);
1246 lt_tag_unref(ntag);
1247 goto bail1;
1248 }
1249 _lt_tag_subtract(tag, rtag);
1250 _lt_tag_replace(tag, ntag);
1251 lt_tag_unref(rtag);
1252 lt_tag_unref(ntag);
1253 }
1254 break;
1255 } else {
1256 if (!lt_tag_truncate(ctag, &err))
1257 goto bail1;
1258 }
1259 }
1260
1261 if (tag->language) {
1262 gsize len;
1263 lt_extlang_db_t *edb = lt_db_get_extlang();
1264 lt_extlang_t *e;
1265
1266 /* If the language tag starts with a primary language subtag
1267 * that is also an extlang subtag, then the language tag is
1268 * prepended with the extlang's 'Prefix'.
1269 */
1270 e = lt_extlang_db_lookup(edb, lt_lang_get_better_tag(tag->language));
1271 if (e) {
1272 const gchar *prefix = lt_extlang_get_prefix(e);
1273
1274 if (prefix)
1275 g_string_append_printf(string, "%s-", prefix);
1276 lt_extlang_unref(e);
1277 }
1278 lt_extlang_db_unref(edb);
1279
1280 g_string_append(string, lt_lang_get_better_tag(tag->language));
1281 if (tag->extlang) {
1282 const gchar *preferred = lt_extlang_get_preferred_tag(tag->extlang);
1283
1284 if (preferred) {
1285 g_string_truncate(string, 0);
1286 g_string_append(string, preferred);
1287 } else {
1288 g_string_append_printf(string, "-%s",
1289 lt_extlang_get_tag(tag->extlang));
1290 }
1291 }
1292 if (tag->script) {
1293 const gchar *script = lt_script_get_tag(tag->script);
1294 const gchar *suppress = lt_lang_get_suppress_script(tag->language);
1295
1296 if (!suppress ||
1297 g_ascii_strcasecmp(suppress, script))
1298 g_string_append_printf(string, "-%s", script);
1299 }
1300 if (tag->region) {
1301 g_string_append_printf(string, "-%s", lt_region_get_better_tag(tag->region));
1302 }
1303 l = tag->variants;
1304 len = string->len;
1305 while (l != NULL((void*)0)) {
1306 lt_variant_t *variant = l->data;
1307 const gchar *better = lt_variant_get_better_tag(variant);
1308 const gchar *s = lt_variant_get_tag(variant);
1309
1310 if (better && g_ascii_strcasecmp(s, better) != 0) {
1311 /* ignore all of variants prior to this one */
1312 g_string_truncate(string, len);
1313 }
1314 g_string_append_printf(string, "-%s", better ? better : s);
1315 l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0));
1316 }
1317 if (tag->extension) {
1318 gchar *s = lt_extension_get_canonicalized_tag(tag->extension);
1319
1320 g_string_append_printf(string, "-%s", s);
1321 g_free(s);
1322 }
1323 }
1324 if (tag->privateuse && tag->privateuse->len > 0) {
1325 g_string_append_printf(string, "%s%s",
1326 string->len > 0 ? "-" : "",
1327 tag->privateuse->str);
1328 }
1329 if (string->len == 0) {
1330 g_set_error(&err, LT_ERROR(lt_error_get_quark()), LT_ERR_NO_TAG,
1331 "No tag to convert.");
1332 }
1333 bail1:
1334 if (ctag)
1335 lt_tag_unref(ctag);
1336 if (rdb)
1337 lt_redundant_db_unref(rdb);
1338 if (r)
1339 lt_redundant_unref(r);
1340 retval = g_string_free(string, FALSE(0));
1341 if (err) {
1342 if (error)
1343 *error = g_error_copy(err);
1344 else
1345 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
1346 g_error_free(err);
1347 if (retval)
1348 g_free(retval);
1349 retval = NULL((void*)0);
1350 }
1351
1352 return retval;
1353}
1354
1355/**
1356 * lt_tag_convert_to_locale:
1357 * @tag: a #lt_tag_t.
1358 * @error: (allow-none): a #GError or %NULL.
1359 *
1360 * Convert the language tag to the locale.
1361 *
1362 * Returns: a locale string or %NULL if fails
1363 */
1364gchar *
1365lt_tag_convert_to_locale(lt_tag_t *tag,
1366 GError **error)
1367{
1368 gchar *retval = NULL((void*)0);
1369 GString *string = NULL((void*)0);
1370 GError *err = NULL((void*)0);
1371 const gchar *mod = NULL((void*)0);
1372 gchar *canonical_tag = NULL((void*)0);
1373 lt_tag_t *ctag;
1374
1375 g_return_val_if_fail (tag != NULL, NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return (((void
*)0)); }; }while (0)
;
1376
1377 canonical_tag = lt_tag_canonicalize(tag, &err);
1378 if (!canonical_tag)
1
Taking false branch
1379 goto bail;
1380 ctag = lt_tag_new();
1381 if (!lt_tag_parse(ctag, canonical_tag, &err)) {
2
Taking false branch
1382 lt_tag_unref(ctag);
1383 goto bail;
1384 }
1385 string = g_string_new(NULL((void*)0));
1386 g_string_append(string, lt_lang_get_better_tag(ctag->language));
3
Access to field 'language' results in a dereference of a null pointer (loaded from variable 'ctag')
1387 if (ctag->region)
1388 g_string_append_printf(string, "_%s",
1389 lt_region_get_tag(ctag->region));
1390 if (ctag->script) {
1391 mod = lt_script_convert_to_modifier(ctag->script);
1392 if (mod)
1393 g_string_append_printf(string, "@%s", mod);
1394 }
1395 lt_tag_unref(ctag);
1396
1397 bail:
1398 g_free(canonical_tag);
1399 if (string)
1400 retval = g_string_free(string, FALSE(0));
1401 if (err) {
1402 if (error)
1403 *error = g_error_copy(err);
1404 else
1405 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
1406 g_error_free(err);
1407 if (retval)
1408 g_free(retval);
1409 retval = NULL((void*)0);
1410 }
1411
1412 return retval;
1413}
1414
1415/**
1416 * lt_tag_dump:
1417 * @tag: a #lt_tag_t.
1418 *
1419 * Dumps the container information to the standard output.
1420 */
1421void
1422lt_tag_dump(const lt_tag_t *tag)
1423{
1424 GList *l;
1425
1426 g_return_if_fail (tag != NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return; }; }
while (0)
;
1427
1428 if (tag->grandfathered) {
1429 lt_grandfathered_dump(tag->grandfathered);
1430 return;
1431 }
1432 lt_lang_dump(tag->language);
1433 if (tag->extlang)
1434 lt_extlang_dump(tag->extlang);
1435 if (tag->script)
1436 lt_script_dump(tag->script);
1437 if (tag->region)
1438 lt_region_dump(tag->region);
1439 l = tag->variants;
1440 while (l != NULL((void*)0)) {
1441 lt_variant_t *variant = l->data;
1442
1443 lt_variant_dump(variant);
1444 l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0));
1445 }
1446 if (tag->extension)
1447 lt_extension_dump(tag->extension);
1448 if (tag->privateuse->len > 0)
1449 g_print("Private Use: %s\n", tag->privateuse->str);
1450}
1451
1452/**
1453 * lt_tag_compare:
1454 * @v1: a #lt_tag_t.
1455 * @v2: a #lt_tag_t.
1456 *
1457 * Compare if @v1 and @v2 is the same object or not.
1458 *
1459 * Returns: %TRUE if it's the same, otherwise %FALSE.
1460 */
1461gboolean
1462lt_tag_compare(const lt_tag_t *v1,
1463 const lt_tag_t *v2)
1464{
1465 gboolean retval = TRUE(!(0));
1466 const GList *l1, *l2;
1467
1468 g_return_val_if_fail (v1 != NULL, FALSE)do{ if (v1 != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v1 != NULL"); return ((0))
; }; }while (0)
;
1469 g_return_val_if_fail (v2 != NULL, FALSE)do{ if (v2 != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v2 != NULL"); return ((0))
; }; }while (0)
;
1470 g_return_val_if_fail (v1->grandfathered == NULL, FALSE)do{ if (v1->grandfathered == ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v1->grandfathered == NULL"
); return ((0)); }; }while (0)
;
1471 g_return_val_if_fail (v2->grandfathered == NULL, FALSE)do{ if (v2->grandfathered == ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v2->grandfathered == NULL"
); return ((0)); }; }while (0)
;
1472
1473 retval &= lt_lang_compare(v1->language, v2->language);
1474 if (v2->extlang)
1475 retval &= lt_extlang_compare(v1->extlang, v2->extlang);
1476 if (v2->script)
1477 retval &= lt_script_compare(v1->script, v2->script);
1478 if (v2->region)
1479 retval &= lt_region_compare(v1->region, v2->region);
1480 l1 = v1->variants;
1481 l2 = v2->variants;
1482 while (l2 != NULL((void*)0)) {
1483 lt_variant_t *vv1, *vv2;
1484
1485 vv1 = l1 ? l1->data : NULL((void*)0);
1486 vv2 = l2->data;
1487 retval &= lt_variant_compare(vv1, vv2);
1488 l1 = l1 ? g_list_next(l1)((l1) ? (((GList *)(l1))->next) : ((void*)0)) : NULL((void*)0);
1489 l2 = g_list_next(l2)((l2) ? (((GList *)(l2))->next) : ((void*)0));
1490 }
1491 if (v2->extension)
1492 retval &= lt_extension_compare(v1->extension, v2->extension);
1493 if (v2->privateuse && v2->privateuse->len > 0)
1494 retval &= _lt_tag_gstring_compare(v1->privateuse, v2->privateuse);
1495
1496 return retval;
1497}
1498
1499/**
1500 * lt_tag_match:
1501 * @v1: a #lt_tag_t.
1502 * @v2: a language range string.
1503 * @error: (allow-none): a #GError or %NULL.
1504 *
1505 * Try matching of @v1 and @v2. any of subtags in @v2 is allowed to use
1506 * the wildcard according to the syntax in RFC 4647.
1507 *
1508 * Returns: %TRUE if it matches, otherwise %FALSE.
1509 */
1510gboolean
1511lt_tag_match(const lt_tag_t *v1,
1512 const gchar *v2,
1513 GError **error)
1514{
1515 gboolean retval = FALSE(0);
1516 lt_tag_t *t2 = NULL((void*)0);
1517 lt_tag_state_t state = STATE_NONE;
1518 GError *err = NULL((void*)0);
1519
1520 g_return_val_if_fail (v1 != NULL, FALSE)do{ if (v1 != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v1 != NULL"); return ((0))
; }; }while (0)
;
1521 g_return_val_if_fail (v2 != NULL, FALSE)do{ if (v2 != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "v2 != NULL"); return ((0))
; }; }while (0)
;
1522
1523 t2 = lt_tag_new();
1524 state = lt_tag_parse_wildcard(t2, v2, &err);
1525 if (err)
1526 goto bail;
1527 retval = _lt_tag_match(v1, t2, state);
1528 bail:
1529 if (err) {
1530 if (error)
1531 *error = g_error_copy(err);
1532 else
1533 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
1534 g_error_free(err);
1535 retval = FALSE(0);
1536 }
1537 if (t2)
1538 lt_tag_unref(t2);
1539
1540 return retval;
1541}
1542
1543/**
1544 * lt_tag_lookup:
1545 * @tag: a #lt_tag_t.
1546 * @pattern: a language range string.
1547 * @error: (allow-none): a #GError or %NULL.
1548 *
1549 * Lookup the language tag that @tag meets with @pattern.
1550 * Any of subtags in @pattern is allowed to use the wildcard according to
1551 * the syntax in RFC 4647.
1552 *
1553 * Returns: a language tag string if any matches, otherwise %NULL.
1554 */
1555gchar *
1556lt_tag_lookup(const lt_tag_t *tag,
1557 const gchar *pattern,
1558 GError **error)
1559{
1560 lt_tag_t *t2 = NULL((void*)0);
1561 lt_tag_state_t state = STATE_NONE;
1562 GError *err = NULL((void*)0);
1563 GList *l;
1564 gchar *retval = NULL((void*)0);
1565
1566 g_return_val_if_fail (tag != NULL, NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return (((void
*)0)); }; }while (0)
;
1567 g_return_val_if_fail (pattern != NULL, NULL)do{ if (pattern != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "pattern != NULL"); return (
((void*)0)); }; }while (0)
;
1568
1569 t2 = lt_tag_new();
1570 state = lt_tag_parse_wildcard(t2, pattern, &err);
1571 if (err)
1572 goto bail;
1573 if (_lt_tag_match(tag, t2, state)) {
1574 gint32 i;
1575
1576 for (i = 0; i < (STATE_END - 1); i++) {
1577 if (t2->wildcard_map & (1 << i)) {
1578 switch (i + 1) {
1579 case STATE_LANG:
1580 lt_tag_set_language(t2, lt_lang_ref(tag->language));
1581 break;
1582 case STATE_EXTLANG:
1583 lt_tag_free_extlang(t2);
1584 if (tag->extlang) {
1585 lt_tag_set_extlang(t2, lt_extlang_ref(tag->extlang));
1586 }
1587 break;
1588 case STATE_SCRIPT:
1589 lt_tag_free_script(t2);
1590 if (tag->script) {
1591 lt_tag_set_script(t2, lt_script_ref(tag->script));
1592 }
1593 break;
1594 case STATE_REGION:
1595 lt_tag_free_region(t2);
1596 if (tag->region) {
1597 lt_tag_set_region(t2, lt_region_ref(tag->region));
1598 }
1599 break;
1600 case STATE_VARIANT:
1601 lt_tag_free_variants(t2);
1602 l = tag->variants;
1603 while (l != NULL((void*)0)) {
1604 lt_tag_set_variant(t2, lt_variant_ref(l->data));
1605 l = g_list_next(l)((l) ? (((GList *)(l))->next) : ((void*)0));
1606 }
1607 break;
1608 case STATE_EXTENSION:
1609 case STATE_EXTENSIONTOKEN:
1610 case STATE_EXTENSIONTOKEN2:
1611 lt_tag_free_extension(t2);
1612 if (tag->extension) {
1613 lt_tag_set_extension(t2, lt_extension_ref(tag->extension));
1614 }
1615 break;
1616 case STATE_PRIVATEUSE:
1617 case STATE_PRIVATEUSETOKEN:
1618 case STATE_PRIVATEUSETOKEN2:
1619 if (t2->privateuse) {
1620 g_string_truncate(t2->privateuse, 0);
1621 }
1622 if (tag->privateuse) {
1623 g_string_append(t2->privateuse, tag->privateuse->str);
1624 }
1625 break;
1626 default:
1627 break;
1628 }
1629 }
1630 }
1631 lt_tag_free_tag_string(t2);
1632 retval = g_strdup(lt_tag_get_string(t2));
1633 }
1634 bail:
1635 if (err) {
1636 if (error)
1637 *error = g_error_copy(err);
1638 else
1639 g_warning(err->message)g_log ("LangTag", G_LOG_LEVEL_WARNING, err->message);
1640 g_error_free(err);
1641 }
1642 if (t2)
1643 lt_tag_unref(t2);
1644
1645 return retval;
1646}
1647
1648#define DEFUNC_GET_SUBTAG(__func__,__type__) \
1649 const __type__ * \
1650 lt_tag_get_ ##__func__ (const lt_tag_t *tag) \
1651 { \
1652 g_return_val_if_fail (tag != NULL, NULL)do{ if (tag != ((void*)0)) { } else { g_return_if_fail_warning
("LangTag", __PRETTY_FUNCTION__, "tag != NULL"); return (((void
*)0)); }; }while (0)
; \
1653 \
1654 return tag->__func__; \
1655 }
1656
1657/**
1658 * lt_tag_get_language:
1659 * @tag: a #lt_tag_t.
1660 *
1661 * Obtain a #lt_lang_t instance in @tag.
1662 *
1663 * Returns: (transfer none): a #lt_lang_t.
1664 */
1665DEFUNC_GET_SUBTAG (language, lt_lang_t)
1666/**
1667 * lt_tag_get_extlang:
1668 * @tag: a #lt_tag_t.
1669 *
1670 * Obtain a #lt_extlang_t instance in @tag.
1671 *
1672 * Returns: (transfer none): a #lt_extlang_t.
1673 */
1674DEFUNC_GET_SUBTAG (extlang, lt_extlang_t)
1675/**
1676 * lt_tag_get_script:
1677 * @tag: a #lt_tag_t.
1678 *
1679 * Obtain a #lt_script_t instance in @tag.
1680 *
1681 * Returns: (transfer none): a #lt_script_t.
1682 */
1683DEFUNC_GET_SUBTAG (script, lt_script_t)
1684/**
1685 * lt_tag_get_region:
1686 * @tag: a #lt_tag_t.
1687 *
1688 * Obtain a #lt_region_t instance in @tag.
1689 *
1690 * Returns: (transfer none): a #lt_region_t.
1691 */
1692DEFUNC_GET_SUBTAG (region, lt_region_t)
1693/**
1694 * lt_tag_get_variants:
1695 * @tag: a #lt_tag_t.
1696 *
1697 * Obtain a list of #lt_variant_t instance in @tag.
1698 *
1699 * Returns: (element-type lt_variant_t) (transfer none): a #GList containing #lt_variant_t.
1700 */
1701DEFUNC_GET_SUBTAG (variants, GList)
1702/**
1703 * lt_tag_get_extension:
1704 * @tag: a #lt_tag_t.
1705 *
1706 * Obtain a #lt_extension_t instance in @tag.
1707 *
1708 * Returns: (transfer none): a #lt_extension_t.
1709 */
1710DEFUNC_GET_SUBTAG (extension, lt_extension_t)
1711/**
1712 * lt_tag_get_privateuse:
1713 * @tag: a #lt_tag_t.
1714 *
1715 * Obtain a #GString instance in @tag.
1716 *
1717 * Returns: (transfer none): a #GString.
1718 */
1719DEFUNC_GET_SUBTAG (privateuse, GString)
1720/**
1721 * lt_tag_get_grandfathered:
1722 * @tag: a #lt_tag_t.
1723 *
1724 * Obtain a #lt_grandfathered_t instance in @tag.
1725 *
1726 * Returns: (transfer none): a #lt_grandfathered_t.
1727 */
1728DEFUNC_GET_SUBTAG (grandfathered, lt_grandfathered_t)
1729
1730#undef DEFUNC_GET_SUBTAG