~ubuntu-branches/ubuntu/trusty/unity-control-center/trusty

« back to all changes in this revision

Viewing changes to panels/common/gdm-languages.c

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2014-01-08 16:29:18 UTC
  • Revision ID: package-import@ubuntu.com-20140108162918-g29dd08tr913y2qh
Tags: upstream-14.04.0
ImportĀ upstreamĀ versionĀ 14.04.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright 2008  Red Hat, Inc,
 
4
 *           2007  William Jon McCann <mccann@jhu.edu>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
19
 *
 
20
 * Written by : William Jon McCann <mccann@jhu.edu>
 
21
 *              Ray Strode <rstrode@redhat.com>
 
22
 */
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#include <stdlib.h>
 
27
#include <stdio.h>
 
28
#include <unistd.h>
 
29
#include <string.h>
 
30
#include <errno.h>
 
31
#include <dirent.h>
 
32
#include <locale.h>
 
33
#include <langinfo.h>
 
34
#include <sys/stat.h>
 
35
 
 
36
#include <glib.h>
 
37
#include <glib/gi18n.h>
 
38
#include <glib/gstdio.h>
 
39
 
 
40
#include "gdm-languages.h"
 
41
 
 
42
#include <langinfo.h>
 
43
#ifndef __LC_LAST
 
44
#define __LC_LAST       13
 
45
#endif
 
46
#include "locarchive.h"
 
47
 
 
48
#define ALIASES_FILE DATADIR "/gdm/locale.alias"
 
49
#define ARCHIVE_FILE LIBLOCALEDIR "/locale-archive"
 
50
#define SYSTEM_ARCHIVE_FILE "/usr/lib/locale/locale-archive"
 
51
#define ISO_CODES_DATADIR ISO_CODES_PREFIX "/share/xml/iso-codes"
 
52
#define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
 
53
 
 
54
typedef struct _GdmLocale {
 
55
        char *id;
 
56
        char *name;
 
57
        char *language_code;
 
58
        char *territory_code;
 
59
        char *codeset;
 
60
        char *modifier;
 
61
} GdmLocale;
 
62
 
 
63
static GHashTable *gdm_languages_map;
 
64
static GHashTable *gdm_territories_map;
 
65
static GHashTable *gdm_available_locales_map;
 
66
static GHashTable *gdm_language_count_map;
 
67
static GHashTable *gdm_territory_count_map;
 
68
 
 
69
static char * construct_language_name (const char *language,
 
70
                                       const char *territory,
 
71
                                       const char *codeset,
 
72
                                       const char *modifier);
 
73
 
 
74
static gboolean language_name_is_valid (const char *language_name);
 
75
 
 
76
static void
 
77
gdm_locale_free (GdmLocale *locale)
 
78
{
 
79
        if (locale == NULL) {
 
80
                return;
 
81
        }
 
82
 
 
83
        g_free (locale->id);
 
84
        g_free (locale->name);
 
85
        g_free (locale->codeset);
 
86
        g_free (locale->modifier);
 
87
        g_free (locale);
 
88
}
 
89
 
 
90
static char *
 
91
normalize_codeset (const char *codeset)
 
92
{
 
93
        char *normalized_codeset;
 
94
        const char *p;
 
95
        char *q;
 
96
 
 
97
        normalized_codeset = g_strdup (codeset);
 
98
 
 
99
        if (codeset != NULL) {
 
100
                for (p = codeset, q = normalized_codeset;
 
101
                     *p != '\0'; p++) {
 
102
 
 
103
                        if (*p == '-' || *p == '_') {
 
104
                                continue;
 
105
                        }
 
106
 
 
107
                        *q = g_ascii_tolower (*p);
 
108
                        q++;
 
109
                }
 
110
                *q = '\0';
 
111
        }
 
112
 
 
113
        return normalized_codeset;
 
114
}
 
115
 
 
116
/*
 
117
 * According to http://en.wikipedia.org/wiki/Locale
 
118
 * locale names are of the form:
 
119
 * [language[_territory][.codeset][@modifier]]
 
120
 */
 
121
gboolean
 
122
gdm_parse_language_name (const char *name,
 
123
                         char      **language_codep,
 
124
                         char      **territory_codep,
 
125
                         char      **codesetp,
 
126
                         char      **modifierp)
 
127
{
 
128
        GRegex     *re;
 
129
        GMatchInfo *match_info;
 
130
        gboolean    res;
 
131
        GError     *error;
 
132
        gchar      *normalized_codeset = NULL;
 
133
        gchar      *normalized_name = NULL;
 
134
        gboolean    retval;
 
135
 
 
136
        match_info = NULL;
 
137
        retval = FALSE;
 
138
 
 
139
        error = NULL;
 
140
        re = g_regex_new ("^(?P<language>[^_.@[:space:]]+)"
 
141
                          "(_(?P<territory>[[:upper:]]+))?"
 
142
                          "(\\.(?P<codeset>[-_0-9a-zA-Z]+))?"
 
143
                          "(@(?P<modifier>[[:ascii:]]+))?$",
 
144
                          0, 0, &error);
 
145
        if (re == NULL) {
 
146
                g_warning ("%s", error->message);
 
147
                goto out;
 
148
        }
 
149
 
 
150
        if (!g_regex_match (re, name, 0, &match_info) ||
 
151
            g_match_info_is_partial_match (match_info)) {
 
152
                g_warning ("locale '%s' isn't valid\n", name);
 
153
                goto out;
 
154
        }
 
155
 
 
156
        res = g_match_info_matches (match_info);
 
157
        if (! res) {
 
158
                g_warning ("Unable to parse locale: %s", name);
 
159
                goto out;
 
160
        }
 
161
 
 
162
        retval = TRUE;
 
163
 
 
164
        if (language_codep != NULL) {
 
165
                *language_codep = g_match_info_fetch_named (match_info, "language");
 
166
        }
 
167
 
 
168
        if (territory_codep != NULL) {
 
169
                *territory_codep = g_match_info_fetch_named (match_info, "territory");
 
170
 
 
171
                if (*territory_codep != NULL &&
 
172
                    *territory_codep[0] == '\0') {
 
173
                        g_free (*territory_codep);
 
174
                        *territory_codep = NULL;
 
175
                }
 
176
        }
 
177
 
 
178
        if (codesetp != NULL) {
 
179
                *codesetp = g_match_info_fetch_named (match_info, "codeset");
 
180
 
 
181
                if (*codesetp != NULL &&
 
182
                    *codesetp[0] == '\0') {
 
183
                        g_free (*codesetp);
 
184
                        *codesetp = NULL;
 
185
                }
 
186
        }
 
187
 
 
188
        if (modifierp != NULL) {
 
189
                *modifierp = g_match_info_fetch_named (match_info, "modifier");
 
190
 
 
191
                if (*modifierp != NULL &&
 
192
                    *modifierp[0] == '\0') {
 
193
                        g_free (*modifierp);
 
194
                        *modifierp = NULL;
 
195
                }
 
196
        }
 
197
 
 
198
        if (codesetp != NULL && *codesetp != NULL) {
 
199
                normalized_codeset = normalize_codeset (*codesetp);
 
200
                normalized_name = construct_language_name (language_codep ? *language_codep : NULL,
 
201
                                                           territory_codep ? *territory_codep : NULL,
 
202
                                                           normalized_codeset,
 
203
                                                           modifierp ? *modifierp : NULL);
 
204
 
 
205
                if (language_name_is_valid (normalized_name)) {
 
206
                        g_free (*codesetp);
 
207
                        *codesetp = normalized_codeset;
 
208
                } else {
 
209
                        g_free (normalized_codeset);
 
210
                }
 
211
                g_free (normalized_name);
 
212
        }
 
213
 
 
214
 out:
 
215
        g_match_info_free (match_info);
 
216
        g_regex_unref (re);
 
217
 
 
218
        return retval;
 
219
}
 
220
 
 
221
static char *
 
222
construct_language_name (const char *language,
 
223
                         const char *territory,
 
224
                         const char *codeset,
 
225
                         const char *modifier)
 
226
{
 
227
        const char *adj_codeset;
 
228
        char *name;
 
229
 
 
230
        g_assert (language[0] != 0);
 
231
        g_assert (territory == NULL || territory[0] != 0);
 
232
        g_assert (codeset == NULL || codeset[0] != 0);
 
233
        g_assert (modifier == NULL || modifier[0] != 0);
 
234
 
 
235
        if (g_strcmp0 (codeset, "utf8") == 0) {
 
236
                adj_codeset = "UTF-8";
 
237
        } else
 
238
                adj_codeset = codeset;
 
239
 
 
240
        name = g_strdup_printf ("%s%s%s%s%s%s%s",
 
241
                                language,
 
242
                                territory != NULL? "_" : "",
 
243
                                territory != NULL? territory : "",
 
244
                                codeset != NULL? "." : "",
 
245
                                codeset != NULL? adj_codeset : "",
 
246
                                modifier != NULL? "@" : "",
 
247
                                modifier != NULL? modifier : "");
 
248
 
 
249
        return name;
 
250
}
 
251
 
 
252
char *
 
253
gdm_normalize_language_name (const char *name)
 
254
{
 
255
        char *normalized_name;
 
256
        char *language_code;
 
257
        char *territory_code;
 
258
        char *codeset;
 
259
        char *modifier;
 
260
 
 
261
        if (name[0] == '\0') {
 
262
                return NULL;
 
263
        }
 
264
 
 
265
        gdm_parse_language_name (name,
 
266
                                 &language_code,
 
267
                                 &territory_code,
 
268
                                 &codeset, &modifier);
 
269
 
 
270
        normalized_name = construct_language_name (language_code,
 
271
                                                   territory_code,
 
272
                                                   codeset, modifier);
 
273
        g_free (language_code);
 
274
        g_free (territory_code);
 
275
        g_free (codeset);
 
276
        g_free (modifier);
 
277
 
 
278
        return normalized_name;
 
279
}
 
280
 
 
281
static gboolean
 
282
language_name_is_valid (const char *language_name)
 
283
{
 
284
        char     *old_locale;
 
285
        gboolean  is_valid;
 
286
#ifdef WITH_INCOMPLETE_LOCALES
 
287
        int lc_type_id = LC_CTYPE;
 
288
#else
 
289
        int lc_type_id = LC_MESSAGES;
 
290
#endif
 
291
 
 
292
        old_locale = g_strdup (setlocale (lc_type_id, NULL));
 
293
        is_valid = setlocale (lc_type_id, language_name) != NULL;
 
294
        setlocale (lc_type_id, old_locale);
 
295
        g_free (old_locale);
 
296
 
 
297
        return is_valid;
 
298
}
 
299
 
 
300
static void
 
301
language_name_get_codeset_details (const char  *language_name,
 
302
                                   char       **pcodeset,
 
303
                                   gboolean    *is_utf8)
 
304
{
 
305
        char     *old_locale;
 
306
        char     *codeset;
 
307
 
 
308
        old_locale = g_strdup (setlocale (LC_CTYPE, NULL));
 
309
 
 
310
        if (setlocale (LC_CTYPE, language_name) == NULL) {
 
311
                g_free (old_locale);
 
312
                return;
 
313
        }
 
314
 
 
315
        codeset = nl_langinfo (CODESET);
 
316
 
 
317
        if (pcodeset != NULL) {
 
318
                *pcodeset = g_strdup (codeset);
 
319
        }
 
320
 
 
321
        if (is_utf8 != NULL) {
 
322
                codeset = normalize_codeset (codeset);
 
323
 
 
324
                *is_utf8 = strcmp (codeset, "utf8") == 0;
 
325
                g_free (codeset);
 
326
        }
 
327
 
 
328
        setlocale (LC_CTYPE, old_locale);
 
329
        g_free (old_locale);
 
330
}
 
331
 
 
332
gboolean
 
333
gdm_language_has_translations (const char *language_name)
 
334
{
 
335
        GDir        *dir;
 
336
        char        *path;
 
337
        const char  *name;
 
338
        gboolean     has_translations;
 
339
 
 
340
        path = g_build_filename (GNOMELOCALEDIR, language_name, "LC_MESSAGES", NULL);
 
341
 
 
342
        has_translations = FALSE;
 
343
        dir = g_dir_open (path, 0, NULL);
 
344
        g_free (path);
 
345
 
 
346
        if (dir == NULL) {
 
347
                goto out;
 
348
        }
 
349
 
 
350
        do {
 
351
                name = g_dir_read_name (dir);
 
352
 
 
353
                if (name == NULL) {
 
354
                        break;
 
355
                }
 
356
 
 
357
                if (g_str_has_suffix (name, ".mo")) {
 
358
                        has_translations = TRUE;
 
359
                        break;
 
360
                }
 
361
        } while (name != NULL);
 
362
        g_dir_close (dir);
 
363
 
 
364
out:
 
365
        return has_translations;
 
366
}
 
367
 
 
368
static gboolean
 
369
add_locale (const char *language_name,
 
370
            gboolean    utf8_only)
 
371
{
 
372
        GdmLocale *locale;
 
373
        GdmLocale *old_locale;
 
374
        char      *name;
 
375
        gboolean   is_utf8 = FALSE;
 
376
 
 
377
        g_return_val_if_fail (language_name != NULL, FALSE);
 
378
        g_return_val_if_fail (*language_name != '\0', FALSE);
 
379
 
 
380
        language_name_get_codeset_details (language_name, NULL, &is_utf8);
 
381
 
 
382
        if (is_utf8) {
 
383
                name = g_strdup (language_name);
 
384
        } else if (utf8_only) {
 
385
                name = g_strdup_printf ("%s.utf8", language_name);
 
386
 
 
387
                language_name_get_codeset_details (name, NULL, &is_utf8);
 
388
                if (!is_utf8) {
 
389
                        g_free (name);
 
390
                        return FALSE;
 
391
                }
 
392
        } else {
 
393
                name = g_strdup (language_name);
 
394
        }
 
395
 
 
396
        if (!language_name_is_valid (name)) {
 
397
                g_debug ("Ignoring '%s' as a locale, since it's invalid", name);
 
398
                g_free (name);
 
399
                return FALSE;
 
400
        }
 
401
 
 
402
        locale = g_new0 (GdmLocale, 1);
 
403
        gdm_parse_language_name (name,
 
404
                                 &locale->language_code,
 
405
                                 &locale->territory_code,
 
406
                                 &locale->codeset,
 
407
                                 &locale->modifier);
 
408
        g_free (name);
 
409
        name = NULL;
 
410
 
 
411
#ifdef WITH_INCOMPLETE_LOCALES
 
412
        if (utf8_only) {
 
413
                if (locale->territory_code == NULL || locale->modifier) {
 
414
                        g_debug ("Ignoring '%s' as a locale, since it lacks territory code or modifier", name);
 
415
                        gdm_locale_free (locale);
 
416
                        return FALSE;
 
417
                }
 
418
        }
 
419
#endif
 
420
 
 
421
        locale->id = construct_language_name (locale->language_code, locale->territory_code,
 
422
                                              NULL, locale->modifier);
 
423
        locale->name = construct_language_name (locale->language_code, locale->territory_code,
 
424
                                                locale->codeset, locale->modifier);
 
425
 
 
426
#ifndef WITH_INCOMPLETE_LOCALES
 
427
        if (!gdm_language_has_translations (locale->name) &&
 
428
            !gdm_language_has_translations (locale->id) &&
 
429
            !gdm_language_has_translations (locale->language_code) &&
 
430
            utf8_only) {
 
431
                g_debug ("Ignoring '%s' as a locale, since it lacks translations", locale->name);
 
432
                gdm_locale_free (locale);
 
433
                return FALSE;
 
434
        }
 
435
#endif
 
436
 
 
437
        if (!utf8_only) {
 
438
                g_free (locale->id);
 
439
                locale->id = g_strdup (locale->name);
 
440
        }
 
441
 
 
442
        old_locale = g_hash_table_lookup (gdm_available_locales_map, locale->id);
 
443
        if (old_locale != NULL) {
 
444
                if (strlen (old_locale->name) > strlen (locale->name)) {
 
445
                        gdm_locale_free (locale);
 
446
                        return FALSE;
 
447
                }
 
448
        }
 
449
 
 
450
        g_hash_table_insert (gdm_available_locales_map, g_strdup (locale->id), locale);
 
451
 
 
452
        return TRUE;
 
453
}
 
454
 
 
455
struct nameent
 
456
{
 
457
        char    *name;
 
458
        uint32_t locrec_offset;
 
459
};
 
460
 
 
461
static gboolean
 
462
collect_locales_from_archive (void)
 
463
{
 
464
        GMappedFile        *mapped;
 
465
        GError             *error;
 
466
        char               *addr;
 
467
        struct locarhead   *head;
 
468
        struct namehashent *namehashtab;
 
469
        struct nameent     *names;
 
470
        uint32_t            used;
 
471
        uint32_t            cnt;
 
472
        gsize               len;
 
473
        gboolean            locales_collected;
 
474
 
 
475
        error = NULL;
 
476
        mapped = g_mapped_file_new (ARCHIVE_FILE, FALSE, &error);
 
477
        if (mapped == NULL) {
 
478
                mapped = g_mapped_file_new (SYSTEM_ARCHIVE_FILE, FALSE, NULL);
 
479
                if (mapped == NULL) {
 
480
                        g_warning ("Mapping failed for %s: %s", ARCHIVE_FILE, error->message);
 
481
                        g_error_free (error);
 
482
                        return FALSE;
 
483
                }
 
484
                g_error_free (error);
 
485
        }
 
486
 
 
487
        locales_collected = FALSE;
 
488
 
 
489
        addr = g_mapped_file_get_contents (mapped);
 
490
        len = g_mapped_file_get_length (mapped);
 
491
 
 
492
        head = (struct locarhead *) addr;
 
493
        if (head->namehash_offset + head->namehash_size > len
 
494
            || head->string_offset + head->string_size > len
 
495
            || head->locrectab_offset + head->locrectab_size > len
 
496
            || head->sumhash_offset + head->sumhash_size > len) {
 
497
                goto out;
 
498
        }
 
499
 
 
500
        namehashtab = (struct namehashent *) (addr + head->namehash_offset);
 
501
 
 
502
        names = (struct nameent *) g_new0 (struct nameent, head->namehash_used);
 
503
        for (cnt = used = 0; cnt < head->namehash_size; ++cnt) {
 
504
                if (namehashtab[cnt].locrec_offset != 0) {
 
505
                        names[used].name = addr + namehashtab[cnt].name_offset;
 
506
                        names[used++].locrec_offset = namehashtab[cnt].locrec_offset;
 
507
                }
 
508
        }
 
509
 
 
510
        for (cnt = 0; cnt < used; ++cnt) {
 
511
                add_locale (names[cnt].name, TRUE);
 
512
        }
 
513
 
 
514
        g_free (names);
 
515
 
 
516
        locales_collected = TRUE;
 
517
 out:
 
518
 
 
519
        g_mapped_file_unref (mapped);
 
520
        return locales_collected;
 
521
}
 
522
 
 
523
static int
 
524
select_dirs (const struct dirent *dirent)
 
525
{
 
526
        int result = 0;
 
527
 
 
528
        if (strcmp (dirent->d_name, ".") != 0 && strcmp (dirent->d_name, "..") != 0) {
 
529
                mode_t mode = 0;
 
530
 
 
531
#ifdef _DIRENT_HAVE_D_TYPE
 
532
                if (dirent->d_type != DT_UNKNOWN && dirent->d_type != DT_LNK) {
 
533
                        mode = DTTOIF (dirent->d_type);
 
534
                } else
 
535
#endif
 
536
                        {
 
537
                                struct stat st;
 
538
                                char       *path;
 
539
 
 
540
                                path = g_build_filename (LIBLOCALEDIR, dirent->d_name, NULL);
 
541
                                if (g_stat (path, &st) == 0) {
 
542
                                        mode = st.st_mode;
 
543
                                }
 
544
                                g_free (path);
 
545
                        }
 
546
 
 
547
                result = S_ISDIR (mode);
 
548
        }
 
549
 
 
550
        return result;
 
551
}
 
552
 
 
553
static void
 
554
collect_locales_from_directory (void)
 
555
{
 
556
        struct dirent **dirents;
 
557
        int             ndirents;
 
558
        int             cnt;
 
559
 
 
560
        ndirents = scandir (LIBLOCALEDIR, &dirents, select_dirs, alphasort);
 
561
 
 
562
        for (cnt = 0; cnt < ndirents; ++cnt) {
 
563
                add_locale (dirents[cnt]->d_name, TRUE);
 
564
        }
 
565
 
 
566
        if (ndirents > 0) {
 
567
                free (dirents);
 
568
        }
 
569
}
 
570
 
 
571
static void
 
572
collect_locales_from_locale_file (const char *locale_file)
 
573
{
 
574
        FILE *langlist;
 
575
        char curline[256];
 
576
        char *getsret;
 
577
 
 
578
        if (locale_file == NULL)
 
579
                return;
 
580
 
 
581
        langlist = fopen (locale_file, "r");
 
582
 
 
583
        if (langlist == NULL)
 
584
                return;
 
585
 
 
586
        for (;;) {
 
587
                char *name;
 
588
                char *lang;
 
589
                char **lang_list;
 
590
                int i;
 
591
 
 
592
                getsret = fgets (curline, sizeof (curline), langlist);
 
593
                if (getsret == NULL)
 
594
                        break;
 
595
 
 
596
                if (curline[0] <= ' ' ||
 
597
                    curline[0] == '#')
 
598
                        continue;
 
599
 
 
600
                name = strtok (curline, " \t\r\n");
 
601
                if (name == NULL)
 
602
                        continue;
 
603
 
 
604
                lang = strtok (NULL, " \t\r\n");
 
605
                if (lang == NULL)
 
606
                        continue;
 
607
 
 
608
                lang_list = g_strsplit (lang, ",", -1);
 
609
                if (lang_list == NULL)
 
610
                        continue;
 
611
 
 
612
                lang = NULL;
 
613
                for (i = 0; lang_list[i] != NULL; i++) {
 
614
                        if (add_locale (lang_list[i], FALSE)) {
 
615
                                break;
 
616
                        }
 
617
                }
 
618
                g_strfreev (lang_list);
 
619
        }
 
620
 
 
621
        fclose (langlist);
 
622
}
 
623
 
 
624
static void
 
625
count_languages_and_territories (void)
 
626
{
 
627
        gpointer value;
 
628
        GHashTableIter iter;
 
629
 
 
630
        gdm_language_count_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
631
        gdm_territory_count_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
632
 
 
633
        g_hash_table_iter_init (&iter, gdm_available_locales_map);
 
634
        while (g_hash_table_iter_next (&iter, NULL, &value)) {
 
635
                GdmLocale *locale;
 
636
 
 
637
                locale = (GdmLocale *) value;
 
638
 
 
639
                if (locale->language_code != NULL) {
 
640
                        int count;
 
641
 
 
642
                        count = GPOINTER_TO_INT (g_hash_table_lookup (gdm_language_count_map, locale->language_code));
 
643
                        count++;
 
644
                        g_hash_table_insert (gdm_language_count_map, g_strdup (locale->language_code), GINT_TO_POINTER (count));
 
645
                }
 
646
 
 
647
                if (locale->territory_code != NULL) {
 
648
                        int count;
 
649
 
 
650
                        count = GPOINTER_TO_INT (g_hash_table_lookup (gdm_territory_count_map, locale->territory_code));
 
651
                        count++;
 
652
                        g_hash_table_insert (gdm_territory_count_map, g_strdup (locale->territory_code), GINT_TO_POINTER (count));
 
653
                }
 
654
        }
 
655
}
 
656
 
 
657
static void
 
658
collect_locales (void)
 
659
{
 
660
 
 
661
        if (gdm_available_locales_map == NULL) {
 
662
                gdm_available_locales_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdm_locale_free);
 
663
        }
 
664
 
 
665
        if (!collect_locales_from_archive ()) {
 
666
#ifndef WITH_INCOMPLETE_LOCALES
 
667
                g_warning ("Could not read list of available locales from libc, "
 
668
                           "guessing possible locales from available translations, "
 
669
                           "but list may be incomplete!");
 
670
#endif
 
671
        }
 
672
 
 
673
        collect_locales_from_directory ();
 
674
 
 
675
        collect_locales_from_locale_file (ALIASES_FILE);
 
676
 
 
677
        count_languages_and_territories ();
 
678
}
 
679
 
 
680
static gint
 
681
get_language_count (const char *language)
 
682
{
 
683
        if (gdm_language_count_map == NULL) {
 
684
                collect_locales ();
 
685
        }
 
686
 
 
687
        return GPOINTER_TO_INT (g_hash_table_lookup (gdm_language_count_map, language));
 
688
}
 
689
 
 
690
static gboolean
 
691
is_unique_language (const char *language)
 
692
{
 
693
  return get_language_count (language) == 1;
 
694
}
 
695
 
 
696
static gint
 
697
get_territory_count (const char *territory)
 
698
{
 
699
        if (gdm_territory_count_map == NULL) {
 
700
                collect_locales ();
 
701
        }
 
702
 
 
703
        return GPOINTER_TO_INT (g_hash_table_lookup (gdm_territory_count_map, territory));
 
704
}
 
705
 
 
706
static gboolean
 
707
is_unique_territory (const char *territory)
 
708
{
 
709
  return get_territory_count (territory) == 1;
 
710
}
 
711
 
 
712
static gboolean
 
713
is_fallback_language (const char *code)
 
714
{
 
715
        const char *fallback_language_names[] = { "C", "POSIX", NULL };
 
716
        int i;
 
717
 
 
718
        for (i = 0; fallback_language_names[i] != NULL; i++) {
 
719
                if (strcmp (code, fallback_language_names[i]) == 0) {
 
720
                        return TRUE;
 
721
                }
 
722
        }
 
723
 
 
724
        return FALSE;
 
725
}
 
726
 
 
727
static const char *
 
728
get_language (const char *code)
 
729
{
 
730
        const char *name;
 
731
        int         len;
 
732
 
 
733
        g_assert (code != NULL);
 
734
 
 
735
        if (is_fallback_language (code)) {
 
736
                return "Unspecified";
 
737
        }
 
738
 
 
739
        len = strlen (code);
 
740
        if (len != 2 && len != 3) {
 
741
                return NULL;
 
742
        }
 
743
 
 
744
        name = (const char *) g_hash_table_lookup (gdm_languages_map, code);
 
745
 
 
746
        return name;
 
747
}
 
748
 
 
749
static char *
 
750
get_first_item_in_semicolon_list (const char *list)
 
751
{
 
752
    char **items;
 
753
    char  *item;
 
754
 
 
755
    /* Some entries in iso codes have multiple values, separated
 
756
     * by semicolons.  Not really sure which one to pick, so
 
757
     * we just arbitrarily pick the first one.
 
758
     */
 
759
    items = g_strsplit (list, "; ", 2);
 
760
 
 
761
    item = g_strdup (items[0]);
 
762
    g_strfreev (items);
 
763
 
 
764
    return item;
 
765
}
 
766
 
 
767
static char *
 
768
get_translated_language (const char *code,
 
769
                         const char *locale)
 
770
{
 
771
        const char *language;
 
772
        char *name;
 
773
 
 
774
        language = get_language (code);
 
775
 
 
776
        name = NULL;
 
777
        if (language != NULL) {
 
778
                const char  *translated_name;
 
779
                char        *old_locale;
 
780
 
 
781
                if (locale != NULL) {
 
782
                        old_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
 
783
                        setlocale (LC_MESSAGES, locale);
 
784
                }
 
785
 
 
786
                if (is_fallback_language (code)) {
 
787
                        name = g_strdup (_("Unspecified"));
 
788
                } else {
 
789
                        translated_name = dgettext ("iso_639", language);
 
790
                        name = get_first_item_in_semicolon_list (translated_name);
 
791
                }
 
792
 
 
793
                if (locale != NULL) {
 
794
                        setlocale (LC_MESSAGES, old_locale);
 
795
                        g_free (old_locale);
 
796
                }
 
797
        }
 
798
 
 
799
        return name;
 
800
}
 
801
 
 
802
static const char *
 
803
get_territory (const char *code)
 
804
{
 
805
        const char *name;
 
806
        int         len;
 
807
 
 
808
        g_assert (code != NULL);
 
809
 
 
810
        len = strlen (code);
 
811
        if (len != 2 && len != 3) {
 
812
                return NULL;
 
813
        }
 
814
 
 
815
        name = (const char *) g_hash_table_lookup (gdm_territories_map, code);
 
816
 
 
817
        return name;
 
818
}
 
819
 
 
820
static char *
 
821
get_translated_territory (const char *code,
 
822
                          const char *locale)
 
823
{
 
824
        const char *territory;
 
825
        char       *name;
 
826
 
 
827
        territory = get_territory (code);
 
828
 
 
829
        name = NULL;
 
830
        if (territory != NULL) {
 
831
                const char *translated_territory;
 
832
                char       *old_locale;
 
833
 
 
834
                if (locale != NULL) {
 
835
                        old_locale = g_strdup (setlocale (LC_MESSAGES, NULL));
 
836
                        setlocale (LC_MESSAGES, locale);
 
837
                }
 
838
 
 
839
                translated_territory = dgettext ("iso_3166", territory);
 
840
                name = get_first_item_in_semicolon_list (translated_territory);
 
841
 
 
842
                if (locale != NULL) {
 
843
                        setlocale (LC_MESSAGES, old_locale);
 
844
                        g_free (old_locale);
 
845
                }
 
846
        }
 
847
 
 
848
        return name;
 
849
}
 
850
 
 
851
static void
 
852
languages_parse_start_tag (GMarkupParseContext      *ctx,
 
853
                           const char               *element_name,
 
854
                           const char              **attr_names,
 
855
                           const char              **attr_values,
 
856
                           gpointer                  user_data,
 
857
                           GError                  **error)
 
858
{
 
859
        const char *ccode_longB;
 
860
        const char *ccode_longT;
 
861
        const char *ccode;
 
862
        const char *ccode_id;
 
863
        const char *lang_common_name;
 
864
        const char *lang_name;
 
865
 
 
866
        if (! (g_str_equal (element_name, "iso_639_entry") || g_str_equal (element_name, "iso_639_3_entry"))
 
867
            || attr_names == NULL || attr_values == NULL) {
 
868
                return;
 
869
        }
 
870
 
 
871
        ccode = NULL;
 
872
        ccode_longB = NULL;
 
873
        ccode_longT = NULL;
 
874
        ccode_id = NULL;
 
875
        lang_common_name = NULL;
 
876
        lang_name = NULL;
 
877
 
 
878
        while (*attr_names && *attr_values) {
 
879
                if (g_str_equal (*attr_names, "iso_639_1_code")) {
 
880
                        /* skip if empty */
 
881
                        if (**attr_values) {
 
882
                                if (strlen (*attr_values) != 2) {
 
883
                                        return;
 
884
                                }
 
885
                                ccode = *attr_values;
 
886
                        }
 
887
                } else if (g_str_equal (*attr_names, "iso_639_2B_code")) {
 
888
                        /* skip if empty */
 
889
                        if (**attr_values) {
 
890
                                if (strlen (*attr_values) != 3) {
 
891
                                        return;
 
892
                                }
 
893
                                ccode_longB = *attr_values;
 
894
                        }
 
895
                } else if (g_str_equal (*attr_names, "iso_639_2T_code")) {
 
896
                        /* skip if empty */
 
897
                        if (**attr_values) {
 
898
                                if (strlen (*attr_values) != 3) {
 
899
                                        return;
 
900
                                }
 
901
                                ccode_longT = *attr_values;
 
902
                        }
 
903
                } else if (g_str_equal (*attr_names, "id")) {
 
904
                        /* skip if empty */
 
905
                        if (**attr_values) {
 
906
                                if (strlen (*attr_values) != 2 &&
 
907
                                    strlen (*attr_values) != 3) {
 
908
                                        return;
 
909
                                }
 
910
                                ccode_id = *attr_values;
 
911
                        }
 
912
                } else if (g_str_equal (*attr_names, "common_name")) {
 
913
                        /* skip if empty */
 
914
                        if (**attr_values) {
 
915
                                lang_common_name = *attr_values;
 
916
                        }
 
917
                } else if (g_str_equal (*attr_names, "name")) {
 
918
                        lang_name = *attr_values;
 
919
                }
 
920
 
 
921
                ++attr_names;
 
922
                ++attr_values;
 
923
        }
 
924
 
 
925
        if (lang_common_name != NULL) {
 
926
                lang_name = lang_common_name;
 
927
        }
 
928
 
 
929
        if (lang_name == NULL) {
 
930
                return;
 
931
        }
 
932
 
 
933
        if (ccode != NULL) {
 
934
                g_hash_table_insert (gdm_languages_map,
 
935
                                     g_strdup (ccode),
 
936
                                     g_strdup (lang_name));
 
937
        }
 
938
        if (ccode_longB != NULL) {
 
939
                g_hash_table_insert (gdm_languages_map,
 
940
                                     g_strdup (ccode_longB),
 
941
                                     g_strdup (lang_name));
 
942
        }
 
943
        if (ccode_longT != NULL) {
 
944
                g_hash_table_insert (gdm_languages_map,
 
945
                                     g_strdup (ccode_longT),
 
946
                                     g_strdup (lang_name));
 
947
        }
 
948
        if (ccode_id != NULL) {
 
949
                g_hash_table_insert (gdm_languages_map,
 
950
                                     g_strdup (ccode_id),
 
951
                                     g_strdup (lang_name));
 
952
        }
 
953
}
 
954
 
 
955
static void
 
956
territories_parse_start_tag (GMarkupParseContext      *ctx,
 
957
                             const char               *element_name,
 
958
                             const char              **attr_names,
 
959
                             const char              **attr_values,
 
960
                             gpointer                  user_data,
 
961
                             GError                  **error)
 
962
{
 
963
        const char *acode_2;
 
964
        const char *acode_3;
 
965
        const char *ncode;
 
966
        const char *territory_common_name;
 
967
        const char *territory_name;
 
968
 
 
969
        if (! g_str_equal (element_name, "iso_3166_entry") || attr_names == NULL || attr_values == NULL) {
 
970
                return;
 
971
        }
 
972
 
 
973
        acode_2 = NULL;
 
974
        acode_3 = NULL;
 
975
        ncode = NULL;
 
976
        territory_common_name = NULL;
 
977
        territory_name = NULL;
 
978
 
 
979
        while (*attr_names && *attr_values) {
 
980
                if (g_str_equal (*attr_names, "alpha_2_code")) {
 
981
                        /* skip if empty */
 
982
                        if (**attr_values) {
 
983
                                if (strlen (*attr_values) != 2) {
 
984
                                        return;
 
985
                                }
 
986
                                acode_2 = *attr_values;
 
987
                        }
 
988
                } else if (g_str_equal (*attr_names, "alpha_3_code")) {
 
989
                        /* skip if empty */
 
990
                        if (**attr_values) {
 
991
                                if (strlen (*attr_values) != 3) {
 
992
                                        return;
 
993
                                }
 
994
                                acode_3 = *attr_values;
 
995
                        }
 
996
                } else if (g_str_equal (*attr_names, "numeric_code")) {
 
997
                        /* skip if empty */
 
998
                        if (**attr_values) {
 
999
                                if (strlen (*attr_values) != 3) {
 
1000
                                        return;
 
1001
                                }
 
1002
                                ncode = *attr_values;
 
1003
                        }
 
1004
                } else if (g_str_equal (*attr_names, "common_name")) {
 
1005
                        /* skip if empty */
 
1006
                        if (**attr_values) {
 
1007
                                territory_common_name = *attr_values;
 
1008
                        }
 
1009
                } else if (g_str_equal (*attr_names, "name")) {
 
1010
                        territory_name = *attr_values;
 
1011
                }
 
1012
 
 
1013
                ++attr_names;
 
1014
                ++attr_values;
 
1015
        }
 
1016
 
 
1017
        if (territory_common_name != NULL) {
 
1018
                territory_name = territory_common_name;
 
1019
        }
 
1020
 
 
1021
        if (territory_name == NULL) {
 
1022
                return;
 
1023
        }
 
1024
 
 
1025
        if (acode_2 != NULL) {
 
1026
                g_hash_table_insert (gdm_territories_map,
 
1027
                                     g_strdup (acode_2),
 
1028
                                     g_strdup (territory_name));
 
1029
        }
 
1030
        if (acode_3 != NULL) {
 
1031
                g_hash_table_insert (gdm_territories_map,
 
1032
                                     g_strdup (acode_3),
 
1033
                                     g_strdup (territory_name));
 
1034
        }
 
1035
        if (ncode != NULL) {
 
1036
                g_hash_table_insert (gdm_territories_map,
 
1037
                                     g_strdup (ncode),
 
1038
                                     g_strdup (territory_name));
 
1039
        }
 
1040
}
 
1041
 
 
1042
static void
 
1043
languages_variant_init (const char *variant)
 
1044
{
 
1045
        GError  *error;
 
1046
        gboolean res;
 
1047
        char    *buf;
 
1048
        gsize    buf_len;
 
1049
        char    *filename;
 
1050
 
 
1051
        bindtextdomain (variant, ISO_CODES_LOCALESDIR);
 
1052
        bind_textdomain_codeset (variant, "UTF-8");
 
1053
 
 
1054
        error = NULL;
 
1055
        filename = g_strdup_printf (ISO_CODES_DATADIR "/%s.xml", variant);
 
1056
        res = g_file_get_contents (filename,
 
1057
                                   &buf,
 
1058
                                   &buf_len,
 
1059
                                   &error);
 
1060
        if (res) {
 
1061
                GMarkupParseContext *ctx;
 
1062
                GMarkupParser        parser = { languages_parse_start_tag, NULL, NULL, NULL, NULL };
 
1063
 
 
1064
                ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
 
1065
 
 
1066
                error = NULL;
 
1067
                res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
 
1068
 
 
1069
                if (! res) {
 
1070
                        g_warning ("Failed to parse '%s': %s\n",
 
1071
                                   filename,
 
1072
                                   error->message);
 
1073
                        g_error_free (error);
 
1074
                }
 
1075
 
 
1076
                g_markup_parse_context_free (ctx);
 
1077
                g_free (buf);
 
1078
        } else {
 
1079
                g_warning ("Failed to load '%s': %s\n",
 
1080
                           filename,
 
1081
                           error->message);
 
1082
                g_error_free (error);
 
1083
        }
 
1084
 
 
1085
        g_free (filename);
 
1086
}
 
1087
 
 
1088
static void
 
1089
languages_init (void)
 
1090
{
 
1091
        gdm_languages_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
1092
 
 
1093
        languages_variant_init ("iso_639");
 
1094
        languages_variant_init ("iso_639_3");
 
1095
}
 
1096
 
 
1097
static void
 
1098
territories_init (void)
 
1099
{
 
1100
        GError  *error;
 
1101
        gboolean res;
 
1102
        char    *buf;
 
1103
        gsize    buf_len;
 
1104
 
 
1105
        bindtextdomain ("iso_3166", ISO_CODES_LOCALESDIR);
 
1106
        bind_textdomain_codeset ("iso_3166", "UTF-8");
 
1107
 
 
1108
        gdm_territories_map = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
1109
 
 
1110
        error = NULL;
 
1111
        res = g_file_get_contents (ISO_CODES_DATADIR "/iso_3166.xml",
 
1112
                                   &buf,
 
1113
                                   &buf_len,
 
1114
                                   &error);
 
1115
        if (res) {
 
1116
                GMarkupParseContext *ctx;
 
1117
                GMarkupParser        parser = { territories_parse_start_tag, NULL, NULL, NULL, NULL };
 
1118
 
 
1119
                ctx = g_markup_parse_context_new (&parser, 0, NULL, NULL);
 
1120
 
 
1121
                error = NULL;
 
1122
                res = g_markup_parse_context_parse (ctx, buf, buf_len, &error);
 
1123
 
 
1124
                if (! res) {
 
1125
                        g_warning ("Failed to parse '%s': %s\n",
 
1126
                                   ISO_CODES_DATADIR "/iso_3166.xml",
 
1127
                                   error->message);
 
1128
                        g_error_free (error);
 
1129
                }
 
1130
 
 
1131
                g_markup_parse_context_free (ctx);
 
1132
                g_free (buf);
 
1133
        } else {
 
1134
                g_warning ("Failed to load '%s': %s\n",
 
1135
                           ISO_CODES_DATADIR "/iso_3166.xml",
 
1136
                           error->message);
 
1137
                g_error_free (error);
 
1138
        }
 
1139
}
 
1140
 
 
1141
char *
 
1142
gdm_get_language_from_name (const char *name,
 
1143
                            const char *locale)
 
1144
{
 
1145
        GString *full_language;
 
1146
        char *language_code;
 
1147
        char *territory_code;
 
1148
        char *codeset_code;
 
1149
        char *langinfo_codeset;
 
1150
        char *translated_language;
 
1151
        char *translated_territory;
 
1152
        char *modifier;
 
1153
        gboolean is_utf8 = TRUE;
 
1154
 
 
1155
        g_return_val_if_fail (name != NULL, NULL);
 
1156
        g_return_val_if_fail (*name != '\0', NULL);
 
1157
 
 
1158
        translated_territory = NULL;
 
1159
        translated_language = NULL;
 
1160
        langinfo_codeset = NULL;
 
1161
 
 
1162
        full_language = g_string_new (NULL);
 
1163
 
 
1164
        if (gdm_languages_map == NULL) {
 
1165
                languages_init ();
 
1166
        }
 
1167
 
 
1168
        if (gdm_territories_map == NULL) {
 
1169
                territories_init ();
 
1170
        }
 
1171
 
 
1172
        language_code = NULL;
 
1173
        territory_code = NULL;
 
1174
        codeset_code = NULL;
 
1175
        modifier = NULL;
 
1176
 
 
1177
        gdm_parse_language_name (name,
 
1178
                                 &language_code,
 
1179
                                 &territory_code,
 
1180
                                 &codeset_code,
 
1181
                                 &modifier);
 
1182
 
 
1183
        if (language_code == NULL) {
 
1184
                goto out;
 
1185
        }
 
1186
 
 
1187
        translated_language = get_translated_language (language_code, locale);
 
1188
        if (translated_language == NULL) {
 
1189
                goto out;
 
1190
        }
 
1191
 
 
1192
        full_language = g_string_append (full_language, translated_language);
 
1193
 
 
1194
        if (is_unique_language (language_code)) {
 
1195
                goto out;
 
1196
        }
 
1197
 
 
1198
        if (territory_code != NULL) {
 
1199
                translated_territory = get_translated_territory (territory_code, locale);
 
1200
        }
 
1201
        if (translated_territory != NULL) {
 
1202
                g_string_append_printf (full_language,
 
1203
                                        " (%s)",
 
1204
                                        translated_territory);
 
1205
        }
 
1206
 
 
1207
//        language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
 
1208
 
 
1209
        if (codeset_code == NULL && langinfo_codeset != NULL) {
 
1210
            codeset_code = g_strdup (langinfo_codeset);
 
1211
        }
 
1212
 
 
1213
        if (!is_utf8 && codeset_code) {
 
1214
                g_string_append_printf (full_language,
 
1215
                                        " [%s]",
 
1216
                                        codeset_code);
 
1217
        }
 
1218
 
 
1219
        if (modifier != NULL) {
 
1220
                g_string_append_printf (full_language, " - %s", modifier);
 
1221
        }
 
1222
 
 
1223
out:
 
1224
       g_free (language_code);
 
1225
       g_free (territory_code);
 
1226
       g_free (codeset_code);
 
1227
       g_free (langinfo_codeset);
 
1228
       g_free (translated_language);
 
1229
       g_free (translated_territory);
 
1230
       g_free (modifier);
 
1231
 
 
1232
       if (full_language->len == 0) {
 
1233
               g_string_free (full_language, TRUE);
 
1234
               return NULL;
 
1235
       }
 
1236
 
 
1237
       return g_string_free (full_language, FALSE);
 
1238
}
 
1239
 
 
1240
char *
 
1241
gdm_get_region_from_name (const char *name,
 
1242
                          const char *locale)
 
1243
{
 
1244
        GString *full_name;
 
1245
        char *language_code;
 
1246
        char *territory_code;
 
1247
        char *codeset_code;
 
1248
        char *langinfo_codeset;
 
1249
        char *translated_language;
 
1250
        char *translated_territory;
 
1251
        gboolean is_utf8 = TRUE;
 
1252
 
 
1253
        g_return_val_if_fail (name != NULL, NULL);
 
1254
        g_return_val_if_fail (*name != '\0', NULL);
 
1255
 
 
1256
        translated_territory = NULL;
 
1257
        translated_language = NULL;
 
1258
        langinfo_codeset = NULL;
 
1259
 
 
1260
        full_name = g_string_new (NULL);
 
1261
 
 
1262
        if (gdm_languages_map == NULL) {
 
1263
                languages_init ();
 
1264
        }
 
1265
 
 
1266
        if (gdm_territories_map == NULL) {
 
1267
                territories_init ();
 
1268
        }
 
1269
 
 
1270
        language_code = NULL;
 
1271
        territory_code = NULL;
 
1272
        codeset_code = NULL;
 
1273
 
 
1274
        gdm_parse_language_name (name,
 
1275
                                 &language_code,
 
1276
                                 &territory_code,
 
1277
                                 &codeset_code,
 
1278
                                 NULL);
 
1279
 
 
1280
        if (territory_code == NULL) {
 
1281
                goto out;
 
1282
        }
 
1283
 
 
1284
        translated_territory = get_translated_territory (territory_code, locale);
 
1285
        g_string_append (full_name, translated_territory);
 
1286
 
 
1287
        if (is_unique_territory (territory_code)) {
 
1288
                goto out;
 
1289
        }
 
1290
 
 
1291
        if (language_code != NULL) {
 
1292
                translated_language = get_translated_language (language_code, locale);
 
1293
        }
 
1294
        if (translated_language != NULL) {
 
1295
                g_string_append_printf (full_name,
 
1296
                                        " (%s)",
 
1297
                                        translated_language);
 
1298
        }
 
1299
 
 
1300
        language_name_get_codeset_details (name, &langinfo_codeset, &is_utf8);
 
1301
 
 
1302
        if (codeset_code == NULL && langinfo_codeset != NULL) {
 
1303
            codeset_code = g_strdup (langinfo_codeset);
 
1304
        }
 
1305
 
 
1306
        if (!is_utf8 && codeset_code) {
 
1307
                g_string_append_printf (full_name,
 
1308
                                        " [%s]",
 
1309
                                        codeset_code);
 
1310
        }
 
1311
 
 
1312
out:
 
1313
       g_free (language_code);
 
1314
       g_free (territory_code);
 
1315
       g_free (codeset_code);
 
1316
       g_free (langinfo_codeset);
 
1317
       g_free (translated_language);
 
1318
       g_free (translated_territory);
 
1319
 
 
1320
       if (full_name->len == 0) {
 
1321
               g_string_free (full_name, TRUE);
 
1322
               return NULL;
 
1323
       }
 
1324
 
 
1325
       return g_string_free (full_name, FALSE);
 
1326
}
 
1327
 
 
1328
char **
 
1329
gdm_get_all_language_names (void)
 
1330
{
 
1331
        GHashTableIter iter;
 
1332
        gpointer key, value;
 
1333
        GPtrArray *array;
 
1334
 
 
1335
        if (gdm_available_locales_map == NULL) {
 
1336
                collect_locales ();
 
1337
        }
 
1338
 
 
1339
        array = g_ptr_array_new ();
 
1340
        g_hash_table_iter_init (&iter, gdm_available_locales_map);
 
1341
        while (g_hash_table_iter_next (&iter, &key, &value)) {
 
1342
                GdmLocale *locale;
 
1343
 
 
1344
                locale = (GdmLocale *) value;
 
1345
 
 
1346
                g_ptr_array_add (array, g_strdup (locale->name));
 
1347
        }
 
1348
        g_ptr_array_add (array, NULL);
 
1349
 
 
1350
        return (char **) g_ptr_array_free (array, FALSE);
 
1351
}