+static char locale_dir[PATH_MAX];
+
+static void __destroy_lang_info(gpointer data)
+{
+ struct lang_info_s *info = (struct lang_info_s *)data;
+
+ if (info == NULL)
+ return;
+
+ if (info->list)
+ g_list_free_full(info->list, free);
+ if (info->parent)
+ free(info->parent);
+ free(info);
+}
+
+static struct lang_info_s *__create_lang_info(const char *lang)
+{
+ struct lang_info_s *info;
+
+ info = calloc(1, sizeof(struct lang_info_s));
+ if (info == NULL) {
+ _ERR("Out of memory");
+ return NULL;
+ }
+
+ info->parent = strdup(lang);
+ if (info->parent == NULL) {
+ _ERR("Out of memory");
+ free(info);
+ return NULL;
+ }
+
+ return info;
+}
+
+static gint __compare_langs(gconstpointer a, gconstpointer b)
+{
+ if (!a || !b)
+ return -1;
+
+ return strcmp(a, b);
+}
+
+static char *__get_string_before(const char *str, const char *delim)
+{
+ char *new_str;
+ char *dup_str;
+ char *token;
+
+ dup_str = strdup(str);
+ if (dup_str == NULL)
+ return NULL;
+
+ token = strtok(dup_str, delim);
+ if (token == NULL) {
+ free(dup_str);
+ return NULL;
+ }
+
+ new_str = strdup(token);
+ free(dup_str);
+
+ return new_str;
+}
+
+static GHashTable *__get_lang_table(void)
+{
+ GHashTable *table;
+ DIR *dp;
+ struct dirent *dentry;
+ char buf[PATH_MAX];
+ struct stat stat_buf;
+ int ret;
+ char *parent_lang;
+ struct lang_info_s *info;
+
+ if (locale_dir[0] == 0 || locale_dir[0] == '\0')
+ return NULL;
+
+ table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, __destroy_lang_info);
+ if (table == NULL) {
+ _ERR("Out of memory");
+ return NULL;
+ }
+
+ dp = opendir(locale_dir);
+ if (dp == NULL) {
+ g_hash_table_destroy(table);
+ return NULL;
+ }
+
+ while ((dentry = readdir(dp)) != NULL) {
+ if (!strcmp(dentry->d_name, ".") ||
+ !strcmp(dentry->d_name, ".."))
+ continue;
+
+ snprintf(buf, sizeof(buf), "%s/%s", locale_dir, dentry->d_name);
+ ret = stat(buf, &stat_buf);
+ if (ret != 0 || !S_ISDIR(stat_buf.st_mode))
+ continue;
+
+ parent_lang = __get_string_before(dentry->d_name, "_");
+ if (parent_lang == NULL) {
+ _ERR("Out of memory");
+ break;
+ }
+
+ info = g_hash_table_lookup(table, parent_lang);
+ if (info == NULL) {
+ info = __create_lang_info(parent_lang);
+ if (info == NULL) {
+ free(parent_lang);
+ break;
+ }
+ g_hash_table_insert(table, info->parent, info);
+ }
+ info->list = g_list_append(info->list, strdup(dentry->d_name));
+ free(parent_lang);
+ }
+ closedir(dp);
+
+ return table;
+}
+
+static GList *__append_langs(const char *lang, GList *list, GHashTable *table)
+{
+ struct lang_info_s *info;
+ GList *found;
+ char *parent_lang = NULL;
+ char *extract_lang;
+
+ if (lang == NULL)
+ return list;
+
+ extract_lang = __get_string_before(lang, ".");
+ if (extract_lang == NULL)
+ return list;
+
+ found = g_list_find_custom(list, extract_lang, __compare_langs);
+ if (found) {
+ list = g_list_remove_link(list, found);
+ list = g_list_concat(list, found);
+ goto end;
+ }
+
+ parent_lang = __get_string_before(extract_lang, "_");
+ if (parent_lang == NULL)
+ goto end;
+
+ info = g_hash_table_lookup(table, parent_lang);
+ if (info == NULL)
+ goto end;
+
+ found = g_list_find_custom(info->list, extract_lang, __compare_langs);
+ if (found) {
+ info->list = g_list_remove_link(info->list, found);
+ list = g_list_concat(list, found);
+ goto end;
+ }
+
+ found = g_list_find_custom(info->list, parent_lang, __compare_langs);
+ if (found) {
+ info->list = g_list_remove_link(info->list, found);
+ list = g_list_concat(list, found);
+ goto end;
+ }
+
+ found = g_list_first(info->list);
+ if (found) {
+ info->list = g_list_remove_link(info->list, found);
+ list = g_list_concat(list, found);
+ }
+
+end:
+ if (extract_lang)
+ free(extract_lang);
+ if (parent_lang)
+ free(parent_lang);
+
+ return list;
+}
+
+static GList *__split_language(const char *lang)
+{
+ GList *list = NULL;
+ char *dup_lang;
+ char *token;
+
+ dup_lang = strdup(lang);
+ if (dup_lang == NULL) {
+ _ERR("Out of memory");
+ return NULL;
+ }
+
+ token = strtok(dup_lang, ":");
+ while (token != NULL) {
+ list = g_list_append(list, strdup(token));
+ token = strtok(NULL, ":");
+ }
+ free(dup_lang);
+
+ return list;
+}
+
+static GList *__append_default_langs(GList *list)
+{
+ const char *langs[] = {"en_US", "en_GB", "en"};
+ unsigned int i;
+ GList *found;
+
+ for (i = 0; i < (sizeof(langs) / sizeof(langs[0])); i++) {
+ found = g_list_find_custom(list, langs[i], __compare_langs);
+ if (found == NULL)
+ list = g_list_append(list, strdup(langs[i]));
+ }
+
+ return list;
+}
+
+static char *__get_language(const char *lang)
+{
+ GHashTable *table;
+ GList *list;
+ GList *lang_list = NULL;
+ GList *iter;
+ char *language;
+ char buf[LINE_MAX] = {'\0'};
+ size_t n;
+
+ list = __split_language(lang);
+ if (list == NULL)
+ return NULL;
+
+ table = __get_lang_table();
+ if (table == NULL) {
+ g_list_free_full(list, free);
+ return NULL;
+ }
+
+ iter = g_list_first(list);
+ while (iter) {
+ language = (char *)iter->data;
+ lang_list = __append_langs(language, lang_list, table);
+ iter = g_list_next(iter);
+ }
+ g_list_free_full(list, free);
+ g_hash_table_destroy(table);
+
+ lang_list = __append_default_langs(lang_list);
+ iter = g_list_first(lang_list);
+ while (iter) {
+ language = (char *)iter->data;
+ if (language) {
+ if (buf[0] == '\0') {
+ snprintf(buf, sizeof(buf), "%s", language);
+ } else {
+ n = sizeof(buf) - strlen(buf) - 1;
+ strncat(buf, ":", n);
+ n = sizeof(buf) - strlen(buf) - 1;
+ strncat(buf, language, n);
+ }
+ }
+ iter = g_list_next(iter);
+ }
+ g_list_free_full(lang_list, free);
+
+ return strdup(buf);
+}