update for beta release
[framework/uifw/e17.git] / src / bin / e_intl.c
1 #include "e.h"
2
3 static Ecore_Exe *_e_intl_input_method_exec = NULL;
4 static Ecore_Event_Handler *_e_intl_exit_handler = NULL;
5
6 static char *_e_intl_orig_lang = NULL;
7 static char *_e_intl_orig_language = NULL;
8 static char *_e_intl_language = NULL;
9
10 static char *_e_intl_language_alias = NULL;
11
12 static char *_e_intl_orig_xmodifiers = NULL;
13 static char *_e_intl_orig_qt_im_module = NULL;
14 static char *_e_intl_orig_gtk_im_module = NULL;
15 static char *_e_intl_orig_ecore_imf_module = NULL;
16
17 static const char *_e_intl_imc_personal_path = NULL;
18 static const char *_e_intl_imc_system_path = NULL;
19
20 #define E_EXE_STOP(EXE) if (EXE) { ecore_exe_terminate(EXE); ecore_exe_free(EXE); EXE = NULL; }
21 #define E_EXE_IS_VALID(EXE) (!((!EXE) || (EXE[0] == 0)))
22
23 /* All locale parts */
24 #define E_INTL_LOC_ALL          E_INTL_LOC_LANG | \
25                                 E_INTL_LOC_REGION | \
26                                 E_INTL_LOC_CODESET | \
27                                 E_INTL_LOC_MODIFIER
28
29 /* Locale parts which are significant when Validating */
30 #define E_INTL_LOC_SIGNIFICANT  E_INTL_LOC_LANG | \
31                                 E_INTL_LOC_REGION | \
32                                 E_INTL_LOC_CODESET
33
34 /* Language Setting and Listing */
35 static char             *_e_intl_language_path_find(char *language);
36 static Eina_List        *_e_intl_language_dir_scan(const char *dir);
37 static int               _e_intl_language_list_find(Eina_List *language_list, char *language);
38
39 /* Locale Validation and Discovery */
40 static Eina_Hash        *_e_intl_locale_alias_hash_get(void);
41 static char             *_e_intl_locale_alias_get(const char *language);
42 static Eina_List        *_e_intl_locale_system_locales_get(void);
43 static Eina_List        *_e_intl_locale_search_order_get(const char *locale);
44 static int               _e_intl_locale_validate(const char *locale);
45 static void              _e_intl_locale_hash_free(Eina_Hash *language_hash);
46 static Eina_Bool         _e_intl_locale_hash_free_cb(const Eina_Hash *hash, const void *key, void *data, void *fdata);
47
48 /* Input Method Configuration and Management */
49 static Eina_Bool         _e_intl_cb_exit(void *data, int type, void *event);
50 static Eina_List        *_e_intl_imc_dir_scan(const char *dir);
51
52 EINTERN int
53 e_intl_init(void)
54 {
55    char *s;
56
57    e_intl_data_init();
58
59    if ((s = getenv("LANG"))) _e_intl_orig_lang = strdup(s);
60    if ((s = getenv("LANGUAGE"))) _e_intl_orig_language = strdup(s);
61
62    if ((s = getenv("GTK_IM_MODULE"))) _e_intl_orig_gtk_im_module = strdup(s);
63    if ((s = getenv("QT_IM_MODULE"))) _e_intl_orig_qt_im_module = strdup(s);
64    if ((s = getenv("XMODIFIERS"))) _e_intl_orig_xmodifiers = strdup(s);
65    if ((s = getenv("ECORE_IMF_MODULE"))) _e_intl_orig_ecore_imf_module = strdup(s);
66
67    return 1;
68 }
69
70 EINTERN int
71 e_intl_shutdown(void)
72 {
73    E_FREE(_e_intl_language);
74    E_FREE(_e_intl_orig_lang);
75    E_FREE(_e_intl_orig_language);
76
77    E_FREE(_e_intl_orig_gtk_im_module);
78    E_FREE(_e_intl_orig_qt_im_module);
79    E_FREE(_e_intl_orig_xmodifiers);
80    E_FREE(_e_intl_orig_ecore_imf_module);
81
82    if (_e_intl_imc_personal_path)
83      eina_stringshare_del(_e_intl_imc_personal_path);
84    if (_e_intl_imc_system_path)
85      eina_stringshare_del(_e_intl_imc_system_path);
86
87    e_intl_data_shutdown();
88
89    return 1;
90 }
91
92 /* Setup configuration settings and start services */
93 EINTERN int
94 e_intl_post_init(void)
95 {
96    if ((e_config->language) && (e_config->language[0] != 0))
97      e_intl_language_set(e_config->language);
98    else
99      e_intl_language_set(NULL);
100
101    if ((e_config->input_method) && (e_config->input_method[0] != 0))
102      e_intl_input_method_set(e_config->input_method);
103
104    _e_intl_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, 
105                                                   _e_intl_cb_exit, NULL);
106    return 1;
107 }
108
109 EINTERN int
110 e_intl_post_shutdown(void)
111 {
112    if (_e_intl_exit_handler)
113      {
114         ecore_event_handler_del(_e_intl_exit_handler);
115         _e_intl_exit_handler = NULL;
116      }
117
118    e_intl_input_method_set(NULL);
119
120    e_intl_language_set(NULL);
121    E_FREE(_e_intl_language_alias);
122
123    E_EXE_STOP(_e_intl_input_method_exec);
124    return 1;
125 }
126
127 /*
128  * TODO
129  * - Add error dialogs explaining any errors while setting the locale
130  *      * Locale aliases need to be configured
131  *      * Locale is invalid
132  *      * Message files are not found for this locale, then we have (en_US, POSIX, C)
133  * - Add support of compound locales i.e. (en_US;zh_CN;C) ==Defer==
134  * - Add Configuration for to-be-set environment variables
135  */
136 EAPI void
137 e_intl_language_set(const char *lang)
138 {
139    int set_envars;
140    int ok;
141
142    set_envars = 1;
143    /* NULL lang means set everything back to the original environment
144     * defaults
145     */
146    if (!lang)
147      {
148         e_util_env_set("LANG", _e_intl_orig_lang);
149
150         if (!lang) lang = getenv("LC_ALL");
151         if (!lang) lang = getenv("LANG");
152
153         set_envars = 0;
154      }
155
156    E_FREE(_e_intl_language_alias);
157    _e_intl_language_alias = _e_intl_locale_alias_get(lang);
158    E_FREE(_e_intl_language);
159
160    if (lang)
161      _e_intl_language = strdup(lang);
162    else
163      _e_intl_language = NULL;
164
165    ok = 1;
166    if (strcmp(_e_intl_language_alias, "C"))
167      {
168         ok = _e_intl_locale_validate(_e_intl_language_alias);
169         if (!ok)
170           {
171              char *p, *new_lang;
172              
173              new_lang = _e_intl_language_alias;
174              p = strchr(new_lang, '.');
175              if (p) *p = 0;
176              _e_intl_language_alias = strdup(new_lang);
177              E_FREE(new_lang);
178              ok = _e_intl_locale_validate(_e_intl_language_alias);
179           }
180      }
181    if (!ok)
182      {
183         fprintf(stderr, "The locale '%s' cannot be found on your "
184                 "system. Please install this locale or try "
185                 "something else.", _e_intl_language_alias);
186      }
187    else
188      {
189         /* Only set env vars is a non NULL locale was passed */
190         if (set_envars)
191           {
192              e_util_env_set("LANG", lang);
193              /* Unset LANGUAGE, apparently causes issues if set */
194              e_util_env_set("LANGUAGE", NULL);
195           }
196
197         setlocale(LC_ALL, lang);
198         if (_e_intl_language)
199           {
200              char *locale_path;
201
202              locale_path = _e_intl_language_path_find(_e_intl_language_alias);
203              if (!locale_path)
204                {
205                   E_Locale_Parts *locale_parts;
206
207                   locale_parts = e_intl_locale_parts_get(_e_intl_language_alias);
208
209                   /* If locale is C or some form of en don't report an error */
210                   if ((!locale_parts) && 
211                       (strcmp(_e_intl_language_alias, "C")))
212                     {
213                        fprintf(stderr,
214                              "An error occurred setting your locale. \n\n"
215
216                              "The locale you have chosen '%s' appears to \n"
217                              "be an alias, however, it can not be resloved.\n"
218                              "Please make sure you have a 'locale.alias' \n"
219                              "file in your 'messages' path which can resolve\n"
220                              "this alias.\n\n"
221
222                              "Enlightenment will not be translated.\n",
223                              _e_intl_language_alias);
224                     }
225                   else if ((locale_parts) && (locale_parts->lang) && 
226                            (strcmp(locale_parts->lang, "en")))
227                     {
228                        fprintf(stderr,
229                              "An error occurred setting your locale. \n\n"
230
231                              "The translation files for the locale you \n"
232                              "have chosen (%s) cannot be found in your \n"
233                              "'messages' path. \n\n"
234
235                              "Enlightenment will not be translated.\n",
236                              _e_intl_language_alias);
237                     }
238                   e_intl_locale_parts_free(locale_parts);
239                }
240              else
241                {
242 #ifdef HAVE_GETTEXT
243                   bindtextdomain(PACKAGE, locale_path);
244                   textdomain(PACKAGE);
245                   bind_textdomain_codeset(PACKAGE, "UTF-8");
246 #endif
247                   free(locale_path);
248                }
249           }
250      }
251 }
252
253 EAPI const char *
254 e_intl_language_get(void)
255 {
256    return _e_intl_language;
257 }
258
259 EAPI const char *
260 e_intl_language_alias_get(void)
261 {
262    return _e_intl_language_alias;
263 }
264
265 EAPI Eina_List *
266 e_intl_language_list(void)
267 {
268    Eina_List *next;
269    Eina_List *dir_list;
270    Eina_List *all_languages;
271    E_Path_Dir *epd;
272
273    all_languages = NULL;
274    dir_list = e_path_dir_list_get(path_messages);
275    EINA_LIST_FOREACH(dir_list, next, epd)
276      {
277         Eina_List *dir_languages;
278         char *language;
279
280         dir_languages = _e_intl_language_dir_scan(epd->dir);
281
282         EINA_LIST_FREE(dir_languages, language)
283              if ((_e_intl_language_list_find(all_languages, language)) || 
284                  ((strlen(language) > 2) && (!_e_intl_locale_validate(language))))
285                {
286                   free(language);
287                }
288              else
289                all_languages = eina_list_append(all_languages, language);
290           }
291
292    e_path_dir_list_free(dir_list);
293
294    return all_languages;
295 }
296
297 static int
298 _e_intl_language_list_find(Eina_List *language_list, char *language)
299 {
300    Eina_List *l;
301    char *lang;
302
303    if ((!language_list) || (!language)) return 0;
304
305    EINA_LIST_FOREACH(language_list, l, lang)
306      if (!strcmp(lang, language)) return 1;
307
308    return 0;
309 }
310
311 EAPI void
312 e_intl_input_method_set(const char *imc_path)
313 {
314    if (!imc_path)
315      {
316         E_EXE_STOP(_e_intl_input_method_exec);
317         e_util_env_set("GTK_IM_MODULE", _e_intl_orig_gtk_im_module);
318         e_util_env_set("QT_IM_MODULE", _e_intl_orig_qt_im_module);
319         e_util_env_set("XMODIFIERS", _e_intl_orig_xmodifiers);
320         e_util_env_set("ECORE_IMF_MODULE", _e_intl_orig_ecore_imf_module);
321      }
322
323    if (imc_path)
324      {
325         Eet_File *imc_ef;
326         E_Input_Method_Config *imc;
327
328         imc_ef = eet_open(imc_path, EET_FILE_MODE_READ);
329         if (imc_ef)
330           {
331              imc = e_intl_input_method_config_read(imc_ef);
332              eet_close(imc_ef);
333
334              if (imc)
335                {
336                   e_util_env_set("GTK_IM_MODULE", imc->gtk_im_module);
337                   e_util_env_set("QT_IM_MODULE", imc->qt_im_module);
338                   e_util_env_set("XMODIFIERS", imc->xmodifiers);
339                   e_util_env_set("ECORE_IMF_MODULE", imc->ecore_imf_module);
340
341                   E_EXE_STOP(_e_intl_input_method_exec);
342
343                   if (E_EXE_IS_VALID(imc->e_im_exec))
344                     {
345                        e_util_library_path_strip();
346                        _e_intl_input_method_exec = ecore_exe_run(imc->e_im_exec, NULL);
347                        e_util_library_path_restore();
348                        ecore_exe_tag_set(_e_intl_input_method_exec,"E/im_exec");
349
350                        if ((!_e_intl_input_method_exec) ||
351                            (!ecore_exe_pid_get(_e_intl_input_method_exec)))
352                          e_util_dialog_show(_("Input Method Error"),
353                                             _( "Error starting the input method executable<br><br>"
354                                                "please make sure that your input<br>"
355                                                "method configuration is correct and<br>"
356                                                "that your configuration's<br>"
357                                                "executable is in your PATH<br>"));
358                     }
359                   e_intl_input_method_config_free(imc);
360                }
361           }
362      }
363 }
364
365 EAPI Eina_List *
366 e_intl_input_method_list(void)
367 {
368    Eina_List *input_methods;
369    Eina_List *im_list;
370
371    im_list = NULL;
372
373    /* Personal Path */
374    im_list = _e_intl_imc_dir_scan(e_intl_imc_personal_path_get());
375
376    /* System Path */
377    input_methods = _e_intl_imc_dir_scan(e_intl_imc_system_path_get());
378    im_list = eina_list_merge(im_list, input_methods);
379
380    return im_list;
381 }
382
383 const char *
384 e_intl_imc_personal_path_get(void)
385 {
386    if (!_e_intl_imc_personal_path)
387      {
388         char buf[4096];
389
390         e_user_dir_concat_static(buf, "input_methods");
391         _e_intl_imc_personal_path = eina_stringshare_add(buf);
392      }
393    return _e_intl_imc_personal_path;
394 }
395
396 const char *
397 e_intl_imc_system_path_get(void)
398 {
399    if (!_e_intl_imc_system_path)
400      {
401         char buf[4096];
402
403         e_prefix_data_concat_static(buf, "data/input_methods");
404         _e_intl_imc_system_path = eina_stringshare_add(buf);
405      }
406    return _e_intl_imc_system_path;
407 }
408
409 static Eina_Bool
410 _e_intl_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event)
411 {
412    Ecore_Exe_Event_Del *ev;
413
414    ev = event;
415    if (!ev->exe) return ECORE_CALLBACK_PASS_ON;
416
417    if (!(ecore_exe_tag_get(ev->exe) &&
418          (!strcmp(ecore_exe_tag_get(ev->exe), "E/im_exec")))) return 1;
419
420    _e_intl_input_method_exec = NULL;
421    return ECORE_CALLBACK_PASS_ON;
422 }
423
424 static void
425 _e_intl_locale_hash_free(Eina_Hash *locale_hash)
426 {
427    if (!locale_hash) return;
428    eina_hash_foreach(locale_hash, _e_intl_locale_hash_free_cb, NULL);
429    eina_hash_free(locale_hash);
430 }
431
432 static Eina_Bool
433 _e_intl_locale_hash_free_cb(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata __UNUSED__)
434 {
435    free(data);
436    return 1;
437 }
438
439
440 /*
441  * get the directory associated with the language. Language Must be valid alias
442  * i.e. Already validated and already de-aliased.
443  *
444  * NULL means:
445  *  1) The user does not have an enlightenment translation for this locale
446  *  2) The user does not have their locale.aliases configured correctly
447  *
448  * @return NULL if not found.
449  */
450 static char *
451 _e_intl_language_path_find(char *language)
452 {
453    char         *directory;
454    char         *data;
455    Eina_List    *dir_list;
456    Eina_List    *search_list;
457    Eina_List    *next_dir;
458    Eina_List    *next_search;
459    E_Path_Dir   *epd;
460    char         *search_locale;
461    int           found;
462
463    search_list = _e_intl_locale_search_order_get(language);
464
465    if (!search_list) return NULL;
466
467    directory = NULL;
468    found = 0;
469    dir_list = e_path_dir_list_get(path_messages);
470
471    /* For each directory in the path */
472    EINA_LIST_FOREACH(dir_list, next_dir, epd)
473      {
474         if (found) break;
475
476         /* Match canonicalized locale against each possible search */
477         EINA_LIST_FOREACH(search_list, next_search, search_locale)
478           {
479              char message_path[PATH_MAX];
480
481              snprintf(message_path, sizeof(message_path), "%s/%s/LC_MESSAGES/%s.mo", epd->dir, search_locale, PACKAGE);
482
483              if ((ecore_file_exists(message_path)) && (!ecore_file_is_dir(message_path)))
484                {
485                   directory = strdup(epd->dir);
486                   found = 1;
487                }
488           }
489      }
490
491    e_path_dir_list_free(dir_list);
492
493    EINA_LIST_FREE(search_list, data)
494      free(data);
495
496    return directory;
497 }
498
499 static Eina_List *
500 _e_intl_language_dir_scan(const char *dir)
501 {
502    Eina_List *languages;
503    Eina_List *files;
504    char *file;
505
506    languages = NULL;
507
508    files = ecore_file_ls(dir);
509    if (!files) return NULL;
510
511    if (files)
512      {
513         EINA_LIST_FREE(files, file)
514           {
515              char file_path[PATH_MAX];
516
517              snprintf(file_path, sizeof(file_path),"%s/%s/LC_MESSAGES/%s.mo",
518                       dir, file, PACKAGE);
519              if (ecore_file_exists(file_path) && !ecore_file_is_dir(file_path))
520                languages = eina_list_append(languages, file);
521              else
522                free(file);
523           }
524      }
525    return languages;
526 }
527
528 /* get the alias for a locale
529  *
530  * return pointer to allocated alias string. never returns NULL
531  * String will be the same if its a valid locale already or there
532  * is no alias.
533  */
534 static char *
535 _e_intl_locale_alias_get(const char *language)
536 {
537    Eina_Hash *alias_hash;
538    char *alias;
539    char *lower_language;
540    unsigned int i;
541
542    if ((!language) || (!strncmp(language, "POSIX", strlen("POSIX"))))
543      return strdup("C");
544
545    alias_hash = _e_intl_locale_alias_hash_get();
546    if (!alias_hash) /* No alias file available */
547      return strdup(language);
548
549    lower_language = malloc(strlen(language) + 1);
550    for (i = 0; i < strlen(language); i++)
551      lower_language[i] = tolower(language[i]);
552    lower_language[i] = 0;
553
554    alias = eina_hash_find(alias_hash, lower_language);
555    free(lower_language);
556
557    if (alias)
558      alias = strdup(alias);
559    else
560      alias = strdup(language);
561
562    _e_intl_locale_hash_free(alias_hash);
563
564    return alias;
565 }
566
567 static Eina_Hash *
568 _e_intl_locale_alias_hash_get(void)
569 {
570    Eina_List *next;
571    Eina_List *dir_list;
572    E_Path_Dir *epd;
573    Eina_Hash *alias_hash;
574
575    dir_list = e_path_dir_list_get(path_messages);
576    alias_hash = NULL;
577
578    EINA_LIST_FOREACH(dir_list, next, epd)
579      {
580         char buf[4096];
581         FILE *f;
582
583         snprintf(buf, sizeof(buf), "%s/locale.alias", epd->dir);
584         f = fopen(buf, "r");
585         if (f)
586           {
587              char alias[4096], locale[4096];
588
589              /* read locale alias lines */
590              while (fscanf(f, "%4090s %[^\n]\n", alias, locale) == 2)
591                {
592                   /* skip comments */
593                   if ((alias[0] == '!') || (alias[0] == '#'))
594                     continue;
595
596                   /* skip dupes */
597                   if (eina_hash_find(alias_hash, alias))
598                     continue;
599
600                   if (!alias_hash) alias_hash = eina_hash_string_superfast_new(NULL);
601                   eina_hash_add(alias_hash, alias, strdup(locale));
602                }
603              fclose (f);
604           }
605      }
606    e_path_dir_list_free(dir_list);
607
608    return alias_hash;
609 }
610
611 /* return parts of the locale that are requested in the mask
612  * return null if the locale looks to be invalid (Does not have
613  * ll_DD)
614  *
615  * the returned string needs to be freed
616  */
617 /*
618  * Not canonic, just gets the parts
619  */
620 EAPI E_Locale_Parts *
621 e_intl_locale_parts_get(const char *locale)
622 {
623    /* Parse Results */
624    E_Locale_Parts *locale_parts;
625    char  language[4];
626    char  territory[4];
627    char  codeset[32];
628    char  modifier[32];
629
630    /* Parse State */
631    int   state = 0; /* start out looking for the language */
632    unsigned int locale_idx;
633    int   tmp_idx = 0;
634
635    /* Parse Loop - Seperators are _ . @ */
636    for (locale_idx = 0; locale_idx < strlen(locale); locale_idx++)
637      {
638         char locale_char;
639         locale_char = locale[locale_idx];
640
641         /* we have finished scanning the locale string */
642         if (!locale_char)
643           break;
644
645         /* scan this character based on the current start */
646         switch (state)
647           {
648            case 0: /* Gathering Language */
649              if (tmp_idx == 2 && locale_char == '_')
650                {
651                   state++;
652                   language[tmp_idx] = 0;
653                   tmp_idx = 0;
654                }
655              else if ((tmp_idx < 2) && (islower(locale_char)))
656                language[tmp_idx++] = locale_char;
657              else
658                return NULL;
659              break;
660            case 1: /* Gathering Territory */
661              if (tmp_idx == 2 && locale_char == '.')
662                {
663                   state++;
664                   territory[tmp_idx] = 0;
665                   tmp_idx = 0;
666                }
667              else if ((tmp_idx == 2) && (locale_char == '@'))
668                {
669                   state += 2;
670                   territory[tmp_idx] = 0;
671                   codeset[0] = 0;
672                   tmp_idx = 0;
673                }
674              else if ((tmp_idx < 2) && isupper(locale_char))
675                territory[tmp_idx++] = locale_char;
676              else
677                return NULL;
678              break;
679            case 2: /* Gathering Codeset */
680              if (locale_char == '@')
681                {
682                   state++;
683                   codeset[tmp_idx] = 0;
684                   tmp_idx = 0;
685                }
686              else if (tmp_idx < 32)
687                codeset[tmp_idx++] = locale_char;
688              else
689                return NULL;
690              break;
691            case 3: /* Gathering modifier */
692              if (tmp_idx < 32)
693                modifier[tmp_idx++] = locale_char;
694              else
695                return NULL;
696              break;
697            default:
698              break;
699           }
700      }
701
702    /* set end-of-string \0 */
703    /* There are no breaks here on purpose */
704    switch (state)
705      {
706       case 0:
707         language[tmp_idx] = 0;
708         tmp_idx = 0;
709       case 1:
710         territory[tmp_idx] = 0;
711         tmp_idx = 0;
712       case 2:
713         codeset[tmp_idx] = 0;
714         tmp_idx = 0;
715       case 3:
716         modifier[tmp_idx] = 0;
717       default:
718         break;
719      }
720
721    locale_parts = E_NEW(E_Locale_Parts, 1);
722
723    /* Put the parts of the string together */
724    if (language[0] != 0)
725      {
726         locale_parts->mask |= E_INTL_LOC_LANG;
727         locale_parts->lang = eina_stringshare_add(language);
728      }
729    if (territory[0] != 0)
730      {
731         locale_parts->mask |= E_INTL_LOC_REGION;
732         locale_parts->region = eina_stringshare_add(territory);
733      }
734    if (codeset[0] != 0)
735      {
736         locale_parts->mask |= E_INTL_LOC_CODESET;
737         locale_parts->codeset = eina_stringshare_add(codeset);
738      }
739    if (modifier[0] != 0)
740      {
741         locale_parts->mask |= E_INTL_LOC_MODIFIER;
742         locale_parts->modifier = eina_stringshare_add(modifier);
743      }
744
745    return locale_parts;
746 }
747
748 EAPI void
749 e_intl_locale_parts_free(E_Locale_Parts *locale_parts)
750 {
751    if (locale_parts)
752      {
753         if (locale_parts->lang) eina_stringshare_del(locale_parts->lang);
754         if (locale_parts->region) eina_stringshare_del(locale_parts->region);
755         if (locale_parts->codeset) eina_stringshare_del(locale_parts->codeset);
756         if (locale_parts->modifier) eina_stringshare_del(locale_parts->modifier);
757         E_FREE(locale_parts);
758      }
759 }
760
761 EAPI char *
762 e_intl_locale_parts_combine(E_Locale_Parts *locale_parts, int mask)
763 {
764    int locale_size;
765    char *locale;
766
767    if (!locale_parts) return NULL;
768
769    if ((mask & locale_parts->mask) != mask) return NULL;
770
771    /* Construct the clean locale string */
772    locale_size = 0;
773
774    /* determine the size */
775    if (mask & E_INTL_LOC_LANG)
776      locale_size =  strlen(locale_parts->lang) + 1;
777
778    if (mask & E_INTL_LOC_REGION)
779      locale_size += strlen(locale_parts->region) + 1;
780
781    if (mask & E_INTL_LOC_CODESET)
782      locale_size += strlen(locale_parts->codeset) + 1;
783
784    if (mask & E_INTL_LOC_MODIFIER)
785      locale_size += strlen(locale_parts->modifier) + 1;
786
787    if (!locale_size) return NULL;
788
789    /* Allocate memory */
790    locale = (char *) malloc(locale_size);
791    locale[0] = 0;
792
793    if (mask & E_INTL_LOC_LANG)
794      strcat(locale, locale_parts->lang);
795
796    if (mask & E_INTL_LOC_REGION)
797      {
798         if (locale[0] != 0) strcat(locale, "_");
799         strcat(locale, locale_parts->region);
800       }
801
802    if (mask & E_INTL_LOC_CODESET)
803       {
804          if (locale[0] != 0) strcat(locale, ".");
805          strcat(locale, locale_parts->codeset);
806       }
807
808    if (mask & E_INTL_LOC_MODIFIER)
809      {
810         if (locale[0] != 0) strcat(locale, "@");
811         strcat(locale, locale_parts->modifier);
812      }
813
814    return locale;
815 }
816
817 EAPI char *
818 e_intl_locale_charset_canonic_get(const char *charset)
819 {
820    char charset_canonic[32];
821    char c;
822    int i, i_tmp;
823
824    i = 0;
825    i_tmp = 0;
826    while ((c = charset[i++]) != 0)
827      {
828         if (isalnum(c))
829           charset_canonic[i_tmp++] = tolower(c);
830      }
831    charset_canonic[i_tmp] = 0;
832
833    if (!strcmp(charset, charset_canonic))
834      return NULL;
835
836    return strdup(charset_canonic);
837 }
838
839 static Eina_List *
840 _e_intl_locale_system_locales_get(void)
841 {
842    Eina_List    *locales;
843    FILE         *output;
844
845    locales = NULL;
846    output = popen("locale -a", "r");
847    if (output)
848      {
849         char line[32];
850         while (fscanf(output, "%[^\n]\n", line) == 1)
851           locales = eina_list_append(locales, strdup(line));
852
853         pclose(output);
854      }
855    return locales;
856 }
857
858 /*
859  * must be an un aliased locale;
860  */
861 static int
862 _e_intl_locale_validate(const char *locale)
863 {
864    Eina_List *all_locales;
865    E_Locale_Parts *locale_parts;
866    char *locale_next;
867    char *locale_lr;
868    char *locale_cs_canonic;
869    int   found;
870
871    found = 0;
872
873    locale_parts = e_intl_locale_parts_get(locale);
874
875    /* Gather the search information */
876    locale_lr = 
877      e_intl_locale_parts_combine(locale_parts, 
878                                  E_INTL_LOC_LANG | E_INTL_LOC_REGION);
879    if (!locale_lr)
880      {
881         /* Not valid locale, maybe its an alias */
882         locale_lr = strdup(locale);
883         locale_cs_canonic = NULL;
884      }
885    else
886      {
887         if (locale_parts && locale_parts->codeset)
888           locale_cs_canonic = e_intl_locale_charset_canonic_get(locale_parts->codeset);
889         else
890           locale_cs_canonic = NULL;
891      }
892
893    /* Get list of all available locales and aliases */
894    all_locales = _e_intl_locale_system_locales_get();
895
896    /* Match locale with one from the list */
897    EINA_LIST_FREE(all_locales, locale_next)
898      {
899         if (found == 0)
900           {
901              E_Locale_Parts *locale_parts_next;
902              char * locale_lr_next;
903
904              locale_parts_next = e_intl_locale_parts_get(locale_next);
905              locale_lr_next = e_intl_locale_parts_combine(locale_parts_next,
906                    E_INTL_LOC_LANG | E_INTL_LOC_REGION);
907
908              if ((locale_parts) && (locale_lr_next) && 
909                  (!strcmp(locale_lr, locale_lr_next)))
910                {
911                   /* Matched lang/region part, now if CS matches */
912                   if ((!locale_parts->codeset) && (!locale_parts_next->codeset))
913                     {
914                        /* Lang/Region parts match and no charsets,
915                         * we have a match
916                         */
917                        found = 1;
918                     }
919                   else if (locale_parts->codeset && locale_parts_next->codeset)
920                     {
921                        if (!strcmp(locale_parts->codeset, locale_parts_next->codeset))
922                          {
923                             /* Lang/Region and charsets match */
924                             found = 1;
925                          }
926                        else if (locale_cs_canonic)
927                          {
928                             char *locale_cs_canonic_next;
929                             /* try to match charsets in canonic form */
930
931                             locale_cs_canonic_next =
932                                e_intl_locale_charset_canonic_get(locale_parts_next->codeset);
933
934                             if (locale_cs_canonic_next)
935                               {
936                                  if (!strcmp(locale_cs_canonic, locale_cs_canonic_next))
937                                    {
938                                       /* Lang/Resion and charsets in canonic
939                                        * form match
940                                        */
941                                       found = 1;
942                                    }
943                                  free(locale_cs_canonic_next);
944                               }
945                             else
946                               {
947                                  if (!strcmp(locale_cs_canonic, locale_parts_next->codeset))
948                                    {
949                                       /* Lang/Resion and charsets in canonic
950                                        * form match
951                                        */
952                                       found = 1;
953                                    }
954                               }
955                          }
956                     }
957                }
958              else
959                {
960                   /* Its an alias */
961                   if (!strcmp(locale_lr, locale_next)) found = 1;
962                }
963              e_intl_locale_parts_free(locale_parts_next);
964              E_FREE(locale_lr_next);
965           }
966         free (locale_next);
967      }
968    
969    e_intl_locale_parts_free(locale_parts);
970    free(locale_lr);
971    E_FREE(locale_cs_canonic);
972    return found;
973 }
974
975 /*
976  *  arg local must be an already validated and unaliased locale
977  *  returns the locale search order e.g.
978  *  en_US.UTF-8 ->
979  *   Mask (b) Locale (en_US.UTF-8)
980  *   Mask (a) Locale (en_US)
981  *   Mask (9) Locale (en.UTF-8)
982  *   Mask (8) Locale (en)
983  */
984 static Eina_List *
985 _e_intl_locale_search_order_get(const char *locale)
986 {
987    Eina_List *search_list;
988    E_Locale_Parts *locale_parts;
989    char *masked_locale;
990    int mask;
991
992    locale_parts = e_intl_locale_parts_get(locale);
993    if (!locale_parts) return NULL;
994
995    search_list = NULL;
996    for (mask = E_INTL_LOC_ALL; mask >= E_INTL_LOC_LANG; mask--)
997      {
998         if ((mask & locale_parts->mask) == mask)
999           {
1000              /* Only append if the mask we need is available */
1001              masked_locale = e_intl_locale_parts_combine(locale_parts, mask);
1002              search_list = eina_list_append(search_list, masked_locale);
1003           }
1004      }
1005    e_intl_locale_parts_free(locale_parts);
1006    return search_list;
1007 }
1008
1009 static Eina_List *
1010 _e_intl_imc_dir_scan(const char *dir)
1011 {
1012    Eina_List *imcs = NULL;
1013    Eina_List *files;
1014    char *file;
1015
1016    files = ecore_file_ls(dir);
1017
1018    EINA_LIST_FREE(files, file)
1019      {
1020         if (strstr(file, ".imc"))
1021           {
1022              char buf[PATH_MAX];
1023              
1024              snprintf(buf, sizeof(buf), "%s/%s", dir, file);
1025              imcs = eina_list_append(imcs, strdup(buf));
1026           }
1027         free(file);
1028      }
1029    return imcs;
1030 }