File: | liblangtag/unxlngi6.pro/misc/build/liblangtag-0.2/liblangtag/lt-tag.c |
Location: | line 810, column 6 |
Description: | Access to field 'language' results in a dereference of a null pointer (loaded from variable 'rtag') |
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 | */ | |||
44 | typedef 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 | ||||
51 | struct _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 >*/ | |||
66 | static void | |||
67 | _lt_tag_gstring_free(GString *string) | |||
68 | { | |||
69 | g_string_free(string, TRUE(!(0))); | |||
70 | } | |||
71 | ||||
72 | static 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 | ||||
99 | static 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 | ||||
110 | static lt_tag_scanner_t * | |||
111 | lt_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 | ||||
125 | static void | |||
126 | lt_tag_scanner_unref(lt_tag_scanner_t *scanner) | |||
127 | { | |||
128 | if (scanner) | |||
129 | lt_mem_unref(&scanner->parent); | |||
130 | } | |||
131 | ||||
132 | static gboolean | |||
133 | lt_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 | ||||
203 | static gboolean | |||
204 | lt_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 | ||||
213 | static 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 | ||||
230 | DEFUNC_TAG_FREE (language) | |||
231 | DEFUNC_TAG_FREE (extlang) | |||
232 | DEFUNC_TAG_FREE (script) | |||
233 | DEFUNC_TAG_FREE (region) | |||
234 | DEFUNC_TAG_FREE (variants) | |||
235 | DEFUNC_TAG_FREE (extension) | |||
236 | DEFUNC_TAG_FREE (grandfathered) | |||
237 | DEFUNC_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 | ||||
253 | DEFUNC_TAG_SET (language, lt_lang_unref) | |||
254 | DEFUNC_TAG_SET (extlang, lt_extlang_unref) | |||
255 | DEFUNC_TAG_SET (script, lt_script_unref) | |||
256 | DEFUNC_TAG_SET (region, lt_region_unref) | |||
257 | DEFUNC_TAG_SET (extension, lt_extension_unref) | |||
258 | DEFUNC_TAG_SET (grandfathered, lt_grandfathered_unref) | |||
259 | DEFUNC_TAG_SET (tag_string, g_free) | |||
260 | ||||
261 | G_INLINE_FUNCstatic __inline __attribute__ ((unused)) void | |||
262 | lt_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 | ||||
279 | static void | |||
280 | lt_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 | ||||
335 | static gboolean | |||
336 | lt_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 | ||||
390 | static gboolean | |||
391 | lt_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 | ||||
663 | static 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 | ||||
764 | static 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 | ||||
806 | static 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 | ||||
835 | static 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 >*/ | |||
876 | lt_tag_state_t | |||
877 | lt_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 | */ | |||
908 | lt_tag_t * | |||
909 | lt_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 | */ | |||
930 | lt_tag_t * | |||
931 | lt_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 | */ | |||
945 | void | |||
946 | lt_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 | */ | |||
958 | void | |||
959 | lt_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 | */ | |||
986 | gboolean | |||
987 | lt_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 | */ | |||
1004 | lt_tag_t * | |||
1005 | lt_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 | */ | |||
1053 | gboolean | |||
1054 | lt_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 | */ | |||
1150 | const gchar * | |||
1151 | lt_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 | */ | |||
1202 | gchar * | |||
1203 | lt_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 | */ | |||
1364 | gchar * | |||
1365 | lt_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) | |||
1379 | goto bail; | |||
1380 | ctag = lt_tag_new(); | |||
1381 | if (!lt_tag_parse(ctag, canonical_tag, &err)) { | |||
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)); | |||
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 | */ | |||
1421 | void | |||
1422 | lt_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 | */ | |||
1461 | gboolean | |||
1462 | lt_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 | */ | |||
1510 | gboolean | |||
1511 | lt_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 | */ | |||
1555 | gchar * | |||
1556 | lt_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 | */ | |||
1665 | DEFUNC_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 | */ | |||
1674 | DEFUNC_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 | */ | |||
1683 | DEFUNC_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 | */ | |||
1692 | DEFUNC_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 | */ | |||
1701 | DEFUNC_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 | */ | |||
1710 | DEFUNC_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 | */ | |||
1719 | DEFUNC_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 | */ | |||
1728 | DEFUNC_GET_SUBTAG (grandfathered, lt_grandfathered_t) | |||
1729 | ||||
1730 | #undef DEFUNC_GET_SUBTAG |