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