summaryrefslogtreecommitdiff
path: root/hangul
diff options
context:
space:
mode:
Diffstat (limited to 'hangul')
-rw-r--r--hangul/Makefile.am11
-rw-r--r--hangul/hangul.h29
-rw-r--r--hangul/hangulinputcontext.c375
-rw-r--r--hangul/hangulinternals.h31
-rw-r--r--hangul/hangulkeyboard.c950
5 files changed, 1045 insertions, 351 deletions
diff --git a/hangul/Makefile.am b/hangul/Makefile.am
index e3213c4..5031b36 100644
--- a/hangul/Makefile.am
+++ b/hangul/Makefile.am
@@ -12,11 +12,16 @@ noinst_HEADERS = \
libhangul_la_SOURCES = \
hangulctype.c \
hangulinputcontext.c \
+ hangulkeyboard.c \
hanja.c
libhangul_la_CFLAGS = \
- -DLOCALEDIR=\"$(localedir)\" \
- -DLIBHANGUL_DEFAULT_HANJA_DIC=\"$(datadir)/libhangul/hanja/hanja.txt\"
+ -DLOCALEDIR=\"$(localedir)\" \
+ -DLIBHANGUL_DEFAULT_HANJA_DIC=\"$(pkgdatadir)/hanja/hanja.txt\" \
+ -DLIBHANGUL_DATA_DIR=\"$(pkgdatadir)\" \
+ -DTOP_SRCDIR=\"$(abs_top_srcdir)\" \
+ $(EXPAT_CFLAGS) \
+ $(NULL)
libhangul_la_LDFLAGS = -version-info $(LIBHANGUL_CURRENT):$(LIBHANGUL_REVISION):$(LIBHANGUL_AGE)
-libhangul_la_LIBADD =
+libhangul_la_LIBADD = $(EXPAT_LIBS)
diff --git a/hangul/hangul.h b/hangul/hangul.h
index fe85124..447e1e3 100644
--- a/hangul/hangul.h
+++ b/hangul/hangul.h
@@ -84,7 +84,9 @@ enum {
enum {
HANGUL_KEYBOARD_TYPE_JAMO,
HANGUL_KEYBOARD_TYPE_JASO,
- HANGUL_KEYBOARD_TYPE_ROMAJA
+ HANGUL_KEYBOARD_TYPE_ROMAJA,
+ HANGUL_KEYBOARD_TYPE_JAMO_YET,
+ HANGUL_KEYBOARD_TYPE_JASO_YET,
};
enum {
@@ -93,13 +95,19 @@ enum {
HANGUL_IC_OPTION_NON_CHOSEONG_COMBI,
};
+/* library */
+int hangul_init();
+int hangul_fini();
+
/* keyboard */
HangulKeyboard* hangul_keyboard_new(void);
void hangul_keyboard_delete(HangulKeyboard *keyboard);
-void hangul_keyboard_set_value(HangulKeyboard *keyboard,
- int key, ucschar value);
void hangul_keyboard_set_type(HangulKeyboard *keyboard, int type);
+unsigned int hangul_keyboard_list_get_count();
+const char* hangul_keyboard_list_get_keyboard_id(unsigned index_);
+const char* hangul_keyboard_list_get_keyboard_name(unsigned index_);
+
/* combination */
HangulCombination* hangul_combination_new(void);
void hangul_combination_delete(HangulCombination *combination);
@@ -126,15 +134,9 @@ void hangul_ic_set_keyboard(HangulInputContext *hic,
const HangulKeyboard *keyboard);
void hangul_ic_select_keyboard(HangulInputContext *hic,
const char* id);
-void hangul_ic_set_combination(HangulInputContext *hic,
- const HangulCombination *combination);
void hangul_ic_connect_callback(HangulInputContext* hic, const char* event,
void* callback, void* user_data);
-unsigned hangul_ic_get_n_keyboards();
-const char* hangul_ic_get_keyboard_id(unsigned index_);
-const char* hangul_ic_get_keyboard_name(unsigned index_);
-
const ucschar* hangul_ic_get_preedit_string(HangulInputContext *hic);
const ucschar* hangul_ic_get_commit_string(HangulInputContext *hic);
const ucschar* hangul_ic_flush(HangulInputContext *hic);
@@ -166,6 +168,15 @@ const char* hanja_get_comment(const Hanja* hanja);
}
#endif
+void hangul_keyboard_set_value(HangulKeyboard *keyboard,
+ int key, ucschar value) LIBHANGUL_DEPRECATED;
+void hangul_ic_set_combination(HangulInputContext *hic,
+ const HangulCombination *combination) LIBHANGUL_DEPRECATED;
+
+unsigned hangul_ic_get_n_keyboards() LIBHANGUL_DEPRECATED;
+const char* hangul_ic_get_keyboard_id(unsigned index_) LIBHANGUL_DEPRECATED;
+const char* hangul_ic_get_keyboard_name(unsigned index_) LIBHANGUL_DEPRECATED;
+
#undef LIBHANGUL_DEPRECATED
#endif /* libhangul_hangul_h */
diff --git a/hangul/hangulinputcontext.c b/hangul/hangulinputcontext.c
index 83ce52e..504d507 100644
--- a/hangul/hangulinputcontext.c
+++ b/hangul/hangulinputcontext.c
@@ -1,5 +1,5 @@
/* libhangul
- * Copyright (C) 2004 - 2009 Choe Hwanjin
+ * Copyright (C) 2004 - 2016 Choe Hwanjin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -175,8 +175,6 @@
#define FALSE 0
#endif
-#define HANGUL_KEYBOARD_TABLE_SIZE 0x80
-
typedef void (*HangulOnTranslate) (HangulInputContext*,
int,
ucschar*,
@@ -186,26 +184,6 @@ typedef bool (*HangulOnTransition) (HangulInputContext*,
const ucschar*,
void*);
-typedef struct _HangulCombinationItem HangulCombinationItem;
-
-struct _HangulKeyboard {
- int type;
- const char* id;
- const char* name;
- const ucschar* table;
- const HangulCombination* combination;
-};
-
-struct _HangulCombinationItem {
- uint32_t key;
- ucschar code;
-};
-
-struct _HangulCombination {
- int size;
- HangulCombinationItem *table;
-};
-
struct _HangulBuffer {
ucschar choseong;
ucschar jungseong;
@@ -239,113 +217,6 @@ struct _HangulInputContext {
unsigned int option_non_choseong_combi : 1;
};
-#include "hangulkeyboard.h"
-
-static const HangulCombination hangul_combination_default = {
- N_ELEMENTS(hangul_combination_table_default),
- (HangulCombinationItem*)hangul_combination_table_default
-};
-
-static const HangulCombination hangul_combination_romaja = {
- N_ELEMENTS(hangul_combination_table_romaja),
- (HangulCombinationItem*)hangul_combination_table_romaja
-};
-
-static const HangulCombination hangul_combination_full = {
- N_ELEMENTS(hangul_combination_table_full),
- (HangulCombinationItem*)hangul_combination_table_full
-};
-
-static const HangulCombination hangul_combination_ahn = {
- N_ELEMENTS(hangul_combination_table_ahn),
- (HangulCombinationItem*)hangul_combination_table_ahn
-};
-
-static const HangulKeyboard hangul_keyboard_2 = {
- HANGUL_KEYBOARD_TYPE_JAMO,
- "2",
- N_("Dubeolsik"),
- (ucschar*)hangul_keyboard_table_2,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_2y = {
- HANGUL_KEYBOARD_TYPE_JAMO,
- "2y",
- N_("Dubeolsik Yetgeul"),
- (ucschar*)hangul_keyboard_table_2y,
- &hangul_combination_full
-};
-
-static const HangulKeyboard hangul_keyboard_32 = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "32",
- N_("Sebeolsik Dubeol Layout"),
- (ucschar*)hangul_keyboard_table_32,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_390 = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "39",
- N_("Sebeolsik 390"),
- (ucschar*)hangul_keyboard_table_390,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_3final = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "3f",
- N_("Sebeolsik Final"),
- (ucschar*)hangul_keyboard_table_3final,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_3sun = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "3s",
- N_("Sebeolsik Noshift"),
- (ucschar*)hangul_keyboard_table_3sun,
- &hangul_combination_default
-};
-
-static const HangulKeyboard hangul_keyboard_3yet = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "3y",
- N_("Sebeolsik Yetgeul"),
- (ucschar*)hangul_keyboard_table_3yet,
- &hangul_combination_full
-};
-
-static const HangulKeyboard hangul_keyboard_romaja = {
- HANGUL_KEYBOARD_TYPE_ROMAJA,
- "ro",
- N_("Romaja"),
- (ucschar*)hangul_keyboard_table_romaja,
- &hangul_combination_romaja
-};
-
-static const HangulKeyboard hangul_keyboard_ahn = {
- HANGUL_KEYBOARD_TYPE_JASO,
- "ahn",
- N_("Ahnmatae"),
- (ucschar*)hangul_keyboard_table_ahn,
- &hangul_combination_ahn
-};
-
-static const HangulKeyboard* hangul_keyboards[] = {
- &hangul_keyboard_2,
- &hangul_keyboard_2y,
- &hangul_keyboard_390,
- &hangul_keyboard_3final,
- &hangul_keyboard_3sun,
- &hangul_keyboard_3yet,
- &hangul_keyboard_32,
- &hangul_keyboard_romaja,
- &hangul_keyboard_ahn,
-};
-
-
static void hangul_buffer_push(HangulBuffer *buffer, ucschar ch);
static ucschar hangul_buffer_pop (HangulBuffer *buffer);
static ucschar hangul_buffer_peek(HangulBuffer *buffer);
@@ -356,162 +227,6 @@ static int hangul_buffer_get_jamo_string(HangulBuffer *buffer, ucschar *buf,
static void hangul_ic_flush_internal(HangulInputContext *hic);
-HangulKeyboard*
-hangul_keyboard_new()
-{
- HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
- if (keyboard != NULL) {
- ucschar* table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
- if (table != NULL) {
- int i;
- for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; i++)
- table[i] = 0;
-
- keyboard->table = table;
- return keyboard;
- }
- free(keyboard);
- }
-
- return NULL;
-}
-
-static ucschar
-hangul_keyboard_get_value(const HangulKeyboard *keyboard, int key)
-{
- if (keyboard != NULL) {
- if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE)
- return keyboard->table[key];
- }
-
- return 0;
-}
-
-void
-hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
-{
- if (keyboard != NULL) {
- if (key >= 0 && key < HANGUL_KEYBOARD_TABLE_SIZE) {
- ucschar* table = (ucschar*)keyboard->table;
- table[key] = value;
- }
- }
-}
-
-static int
-hangul_keyboard_get_type(const HangulKeyboard *keyboard)
-{
- int type = 0;
- if (keyboard != NULL) {
- type = keyboard->type;
- }
- return type;
-}
-
-void
-hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
-{
- if (keyboard != NULL) {
- keyboard->type = type;
- }
-}
-
-void
-hangul_keyboard_delete(HangulKeyboard *keyboard)
-{
- if (keyboard != NULL)
- free(keyboard);
-}
-
-HangulCombination*
-hangul_combination_new()
-{
- HangulCombination *combination = malloc(sizeof(HangulCombination));
- if (combination != NULL) {
- combination->size = 0;
- combination->table = NULL;
- return combination;
- }
-
- return NULL;
-}
-
-void
-hangul_combination_delete(HangulCombination *combination)
-{
- if (combination != NULL) {
- if (combination->table != NULL)
- free(combination->table);
- free(combination);
- }
-}
-
-static uint32_t
-hangul_combination_make_key(ucschar first, ucschar second)
-{
- return first << 16 | second;
-}
-
-bool
-hangul_combination_set_data(HangulCombination* combination,
- ucschar* first, ucschar* second, ucschar* result,
- unsigned int n)
-{
- if (combination == NULL)
- return false;
-
- if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
- return false;
-
- combination->table = malloc(sizeof(HangulCombinationItem) * n);
- if (combination->table != NULL) {
- int i;
-
- combination->size = n;
- for (i = 0; i < n; i++) {
- combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
- combination->table[i].code = result[i];
- }
- return true;
- }
-
- return false;
-}
-
-static int
-hangul_combination_cmp(const void* p1, const void* p2)
-{
- const HangulCombinationItem *item1 = p1;
- const HangulCombinationItem *item2 = p2;
-
- /* key는 unsigned int이므로 단순히 빼서 리턴하면 안된다.
- * 두 수의 차가 큰 경우 int로 변환하면서 음수가 될 수 있다. */
- if (item1->key < item2->key)
- return -1;
- else if (item1->key > item2->key)
- return 1;
- else
- return 0;
-}
-
-ucschar
-hangul_combination_combine(const HangulCombination* combination,
- ucschar first, ucschar second)
-{
- HangulCombinationItem *res;
- HangulCombinationItem key;
-
- if (combination == NULL)
- return 0;
-
- key.key = hangul_combination_make_key(first, second);
- res = bsearch(&key, combination->table, combination->size,
- sizeof(combination->table[0]), hangul_combination_cmp);
- if (res != NULL)
- return res->code;
-
- return 0;
-}
static bool
hangul_buffer_is_empty(HangulBuffer *buffer)
@@ -845,7 +560,10 @@ hangul_ic_choseong_to_jongseong(HangulInputContext* hic, ucschar cho)
} else {
/* 옛글 조합 규칙을 사용하는 자판의 경우에는 종성이 conjoinable
* 하지 않아도 상관없다 */
- if (hic->keyboard->combination == &hangul_combination_full) {
+ int type = hangul_keyboard_get_type(hic->keyboard);
+ switch (type) {
+ case HANGUL_KEYBOARD_TYPE_JAMO_YET:
+ case HANGUL_KEYBOARD_TYPE_JASO_YET:
return jong;
}
}
@@ -868,8 +586,7 @@ hangul_ic_combine(HangulInputContext* hic, ucschar first, ucschar second)
}
ucschar combined = 0;
- combined = hangul_combination_combine(hic->keyboard->combination,
- first, second);
+ combined = hangul_keyboard_combine(hic->keyboard, 0, first, second);
if (!hic->option_non_choseong_combi) {
if (hangul_is_choseong(first) && hangul_is_choseong(second) &&
@@ -1362,7 +1079,7 @@ hangul_ic_process(HangulInputContext *hic, int ascii)
hic->preedit_string[0] = 0;
hic->commit_string[0] = 0;
- c = hangul_keyboard_get_value(hic->keyboard, ascii);
+ c = hangul_keyboard_get_mapping(hic->keyboard, 0, ascii);
if (hic->on_translate != NULL)
hic->on_translate(hic, ascii, &c, hic->on_translate_data);
@@ -1370,12 +1087,16 @@ hangul_ic_process(HangulInputContext *hic, int ascii)
return hangul_ic_backspace(hic);
}
- if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JAMO)
- return hangul_ic_process_jamo(hic, c);
- else if (hangul_keyboard_get_type(hic->keyboard) == HANGUL_KEYBOARD_TYPE_JASO)
+ int type = hangul_keyboard_get_type(hic->keyboard);
+ switch (type) {
+ case HANGUL_KEYBOARD_TYPE_JASO:
+ case HANGUL_KEYBOARD_TYPE_JASO_YET:
return hangul_ic_process_jaso(hic, c);
- else
+ case HANGUL_KEYBOARD_TYPE_ROMAJA:
return hangul_ic_process_romaja(hic, ascii, c);
+ default:
+ return hangul_ic_process_jamo(hic, c);
+ }
}
/**
@@ -1676,25 +1397,6 @@ hangul_ic_set_keyboard(HangulInputContext *hic, const HangulKeyboard* keyboard)
hic->keyboard = keyboard;
}
-static const HangulKeyboard*
-hangul_ic_get_keyboard_by_id(const char* id)
-{
- unsigned i;
- unsigned n;
-
- /* hangul_keyboards 테이블은 id 순으로 정렬되어 있지 않으므로
- * binary search를 할수 없고 linear search를 한다. */
- n = hangul_ic_get_n_keyboards();
- for (i = 0; i < n; ++i) {
- const HangulKeyboard* keyboard = hangul_keyboards[i];
- if (strcmp(id, keyboard->id) == 0) {
- return keyboard;
- }
- }
-
- return NULL;
-}
-
/**
* @ingroup hangulic
* @brief @ref HangulInputContext 의 자판 배열을 바꾸는 함수
@@ -1729,12 +1431,8 @@ hangul_ic_select_keyboard(HangulInputContext *hic, const char* id)
if (id == NULL)
id = "2";
- keyboard = hangul_ic_get_keyboard_by_id(id);
- if (keyboard != NULL) {
- hic->keyboard = keyboard;
- } else {
- hic->keyboard = &hangul_keyboard_2;
- }
+ keyboard = hangul_keyboard_list_get_keyboard(id);
+ hic->keyboard = keyboard;
}
void
@@ -1811,36 +1509,19 @@ hangul_ic_delete(HangulInputContext *hic)
unsigned int
hangul_ic_get_n_keyboards()
{
- return N_ELEMENTS(hangul_keyboards);
+ return hangul_keyboard_list_get_count();
}
const char*
hangul_ic_get_keyboard_id(unsigned index_)
{
- if (index_ < N_ELEMENTS(hangul_keyboards)) {
- return hangul_keyboards[index_]->id;
- }
-
- return NULL;
+ return hangul_keyboard_list_get_keyboard_id(index_);
}
const char*
hangul_ic_get_keyboard_name(unsigned index_)
{
-#ifdef ENABLE_NLS
- static bool isGettextInitialized = false;
- if (!isGettextInitialized) {
- isGettextInitialized = true;
- bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
- bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
- }
-#endif
-
- if (index_ < N_ELEMENTS(hangul_keyboards)) {
- return _(hangul_keyboards[index_]->name);
- }
-
- return NULL;
+ return hangul_keyboard_list_get_keyboard_name(index_);
}
/**
@@ -1869,3 +1550,19 @@ hangul_ic_is_transliteration(HangulInputContext *hic)
return false;
}
+
+int
+hangul_init()
+{
+ int res;
+ res = hangul_keyboard_list_init();
+ return res;
+}
+
+int
+hangul_fini()
+{
+ int res;
+ res = hangul_keyboard_list_fini();
+ return res;
+}
diff --git a/hangul/hangulinternals.h b/hangul/hangulinternals.h
index 70f8857..8fb0d70 100644
--- a/hangul/hangulinternals.h
+++ b/hangul/hangulinternals.h
@@ -1,7 +1,27 @@
+/* libhangul
+ * Copyright (C) 2016 Choe Hwanjin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
#ifndef libhangul_hangulinternals_h
#define libhangul_hangulinternals_h
#define N_ELEMENTS(array) (sizeof (array) / sizeof ((array)[0]))
+#ifndef countof
+#define countof(array) (sizeof (array) / sizeof ((array)[0]))
+#endif
ucschar hangul_jongseong_get_diff(ucschar prevjong, ucschar jong);
@@ -9,4 +29,15 @@ ucschar hangul_choseong_to_jongseong(ucschar ch);
ucschar hangul_jongseong_to_choseong(ucschar ch);
void hangul_jongseong_decompose(ucschar ch, ucschar* jong, ucschar* cho);
+int hangul_keyboard_get_type(const HangulKeyboard *keyboard);
+ucschar hangul_keyboard_combine(const HangulKeyboard* keyboard,
+ unsigned id, ucschar first, ucschar second);
+ucschar hangul_keyboard_get_mapping(const HangulKeyboard* keyboard,
+ int tableid, unsigned key);
+
+int hangul_keyboard_list_init();
+int hangul_keyboard_list_fini();
+
+const HangulKeyboard* hangul_keyboard_list_get_keyboard(const char* id);
+
#endif /* libhangul_hangulinternals_h */
diff --git a/hangul/hangulkeyboard.c b/hangul/hangulkeyboard.c
new file mode 100644
index 0000000..168774a
--- /dev/null
+++ b/hangul/hangulkeyboard.c
@@ -0,0 +1,950 @@
+/* libhangul
+ * Copyright (C) 2016 Choe Hwanjin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include <locale.h>
+#include <glob.h>
+#include <libgen.h>
+#include <expat.h>
+
+#include "hangul-gettext.h"
+#include "hangul.h"
+#include "hangulinternals.h"
+
+
+#define LIBHANGUL_KEYBOARD_DIR LIBHANGUL_DATA_DIR "/keyboards"
+//#define LIBHANGUL_KEYBOARD_DIR TOP_SRCDIR "/data/keyboards"
+
+#define HANGUL_KEYBOARD_TABLE_SIZE 0x80
+
+typedef struct _HangulCombinationItem HangulCombinationItem;
+
+struct _HangulCombinationItem {
+ uint32_t key;
+ ucschar code;
+};
+
+struct _HangulCombination {
+ size_t size;
+ size_t size_alloced;
+ HangulCombinationItem *table;
+
+ bool is_static;
+};
+
+struct _HangulKeyboard {
+ char* id;
+ char* name;
+ ucschar* table[4];
+ HangulCombination* combination[4];
+
+ int type;
+ bool is_static;
+};
+
+typedef struct _HangulKeyboardList {
+ size_t n;
+ size_t nalloced;
+ HangulKeyboard** keyboards;
+} HangulKeyboardList;
+
+#include "hangulkeyboard.h"
+
+static const HangulCombination hangul_combination_default = {
+ countof(hangul_combination_table_default),
+ countof(hangul_combination_table_default),
+ (HangulCombinationItem*)hangul_combination_table_default,
+ true
+};
+
+static const HangulCombination hangul_combination_romaja = {
+ countof(hangul_combination_table_romaja),
+ countof(hangul_combination_table_romaja),
+ (HangulCombinationItem*)hangul_combination_table_romaja,
+ true
+};
+
+static const HangulCombination hangul_combination_full = {
+ countof(hangul_combination_table_full),
+ countof(hangul_combination_table_full),
+ (HangulCombinationItem*)hangul_combination_table_full,
+ true
+};
+
+static const HangulCombination hangul_combination_ahn = {
+ countof(hangul_combination_table_ahn),
+ countof(hangul_combination_table_ahn),
+ (HangulCombinationItem*)hangul_combination_table_ahn,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_2 = {
+ (char*)"2",
+ (char*)N_("Dubeolsik"),
+ { (ucschar*)hangul_keyboard_table_2, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JAMO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_2y = {
+ (char*)"2y",
+ (char*)N_("Dubeolsik Yetgeul"),
+ { (ucschar*)hangul_keyboard_table_2y, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_full, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JAMO_YET,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_32 = {
+ (char*)"32",
+ (char*)N_("Sebeolsik Dubeol Layout"),
+ { (ucschar*)hangul_keyboard_table_32, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_390 = {
+ (char*)"39",
+ (char*)N_("Sebeolsik 390"),
+ { (ucschar*)hangul_keyboard_table_390, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_3final = {
+ (char*)"3f",
+ (char*)N_("Sebeolsik Final"),
+ { (ucschar*)hangul_keyboard_table_3final, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_3sun = {
+ (char*)"3s",
+ (char*)N_("Sebeolsik Noshift"),
+ { (ucschar*)hangul_keyboard_table_3sun, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_default, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_3yet = {
+ (char*)"3y",
+ (char*)N_("Sebeolsik Yetgeul"),
+ { (ucschar*)hangul_keyboard_table_3yet, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_full, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO_YET,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_romaja = {
+ (char*)"ro",
+ (char*)N_("Romaja"),
+ { (ucschar*)hangul_keyboard_table_romaja, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_romaja, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_ROMAJA,
+ true
+};
+
+static const HangulKeyboard hangul_keyboard_ahn = {
+ (char*)"ahn",
+ (char*)N_("Ahnmatae"),
+ { (ucschar*)hangul_keyboard_table_ahn, NULL, NULL, NULL },
+ { (HangulCombination*)&hangul_combination_ahn, NULL, NULL, NULL },
+ HANGUL_KEYBOARD_TYPE_JASO,
+ true
+};
+
+static const HangulKeyboard* hangul_builtin_keyboards[] = {
+ &hangul_keyboard_2,
+ &hangul_keyboard_2y,
+ &hangul_keyboard_390,
+ &hangul_keyboard_3final,
+ &hangul_keyboard_3sun,
+ &hangul_keyboard_3yet,
+ &hangul_keyboard_32,
+ &hangul_keyboard_romaja,
+ &hangul_keyboard_ahn,
+};
+static unsigned int hangul_builtin_keyboard_count = countof(hangul_builtin_keyboards);
+
+static HangulKeyboardList hangul_keyboards = { 0, 0, NULL };
+
+typedef struct _HangulKeyboardLoadContext {
+ const char* path;
+ HangulKeyboard* keyboard;
+ int current_id;
+ const char* current_element;
+ bool save_name;
+} HangulKeyboardLoadContext;
+
+static void hangul_keyboard_parse_file(const char* path, void* user_data);
+static bool hangul_keyboard_list_append(HangulKeyboard* keyboard);
+
+
+HangulCombination*
+hangul_combination_new()
+{
+ HangulCombination *combination = malloc(sizeof(HangulCombination));
+ if (combination != NULL) {
+ combination->size = 0;
+ combination->size_alloced = 0;
+ combination->table = NULL;
+ combination->is_static = false;
+ return combination;
+ }
+
+ return NULL;
+}
+
+void
+hangul_combination_delete(HangulCombination *combination)
+{
+ if (combination == NULL)
+ return;
+
+ if (combination->is_static)
+ return;
+
+ if (combination->table != NULL)
+ free(combination->table);
+
+ free(combination);
+}
+
+static uint32_t
+hangul_combination_make_key(ucschar first, ucschar second)
+{
+ return first << 16 | second;
+}
+
+bool
+hangul_combination_set_data(HangulCombination* combination,
+ ucschar* first, ucschar* second, ucschar* result,
+ unsigned int n)
+{
+ if (combination == NULL)
+ return false;
+
+ if (n == 0 || n > ULONG_MAX / sizeof(HangulCombinationItem))
+ return false;
+
+ combination->table = malloc(sizeof(HangulCombinationItem) * n);
+ if (combination->table != NULL) {
+ int i;
+
+ combination->size = n;
+ for (i = 0; i < n; i++) {
+ combination->table[i].key = hangul_combination_make_key(first[i], second[i]);
+ combination->table[i].code = result[i];
+ }
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+hangul_combination_add_item(HangulCombination* combination,
+ ucschar first, ucschar second, ucschar result)
+{
+ if (combination == NULL)
+ return false;
+
+ if (combination->is_static)
+ return false;
+
+ if (combination->size >= combination->size_alloced) {
+ size_t size_need = combination->size_alloced * 2;
+ if (size_need == 0) {
+ // 처음 할당할 때에는 64개를 기본값으로 한다.
+ size_need = 64;
+ }
+
+ HangulCombinationItem* table = combination->table;
+ table = realloc(table, size_need * sizeof(table[0]));
+ if (table == NULL)
+ return false;
+
+ combination->size_alloced = size_need;
+ combination->table = table;
+ }
+
+ uint32_t key = hangul_combination_make_key(first, second);
+ size_t i = combination->size;
+ combination->table[i].key = key;
+ combination->table[i].code = result;
+ combination->size = i + 1;
+ return true;
+}
+
+static int
+hangul_combination_cmp(const void* p1, const void* p2)
+{
+ const HangulCombinationItem *item1 = p1;
+ const HangulCombinationItem *item2 = p2;
+
+ /* key는 unsigned int이므로 단순히 빼서 리턴하면 안된다.
+ * 두 수의 차가 큰 경우 int로 변환하면서 음수가 될 수 있다. */
+ if (item1->key < item2->key)
+ return -1;
+ else if (item1->key > item2->key)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+hangul_combination_sort(HangulCombination* combination)
+{
+ if (combination == NULL)
+ return;
+
+ if (combination->is_static)
+ return;
+
+ qsort(combination->table, combination->size,
+ sizeof(combination->table[0]), hangul_combination_cmp);
+}
+
+static ucschar
+hangul_combination_combine(const HangulCombination* combination,
+ ucschar first, ucschar second)
+{
+ HangulCombinationItem *res;
+ HangulCombinationItem key;
+
+ if (combination == NULL)
+ return 0;
+
+ key.key = hangul_combination_make_key(first, second);
+ res = bsearch(&key, combination->table, combination->size,
+ sizeof(combination->table[0]), hangul_combination_cmp);
+ if (res != NULL)
+ return res->code;
+
+ return 0;
+}
+
+HangulKeyboard*
+hangul_keyboard_new()
+{
+ HangulKeyboard *keyboard = malloc(sizeof(HangulKeyboard));
+ if (keyboard == NULL)
+ return NULL;
+
+ keyboard->id = NULL;
+ keyboard->name = NULL;
+
+ keyboard->table[0] = NULL;
+ keyboard->table[1] = NULL;
+ keyboard->table[2] = NULL;
+ keyboard->table[3] = NULL;
+
+ keyboard->combination[0] = NULL;
+ keyboard->combination[1] = NULL;
+ keyboard->combination[2] = NULL;
+ keyboard->combination[3] = NULL;
+
+ keyboard->type = HANGUL_KEYBOARD_TYPE_JAMO;
+ keyboard->is_static = false;
+
+ return keyboard;
+}
+
+static void
+hangul_keyboard_set_id(HangulKeyboard* keyboard, const char* id)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (keyboard->is_static)
+ return;
+
+ free(keyboard->id);
+ keyboard->id = strdup(id);
+}
+
+static void
+hangul_keyboard_set_name(HangulKeyboard* keyboard, const char* name)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (keyboard->is_static)
+ return;
+
+ free(keyboard->name);
+ keyboard->name = strdup(name);
+}
+
+ucschar
+hangul_keyboard_get_mapping(const HangulKeyboard* keyboard, int tableid, unsigned key)
+{
+ if (keyboard == NULL)
+ return 0;
+
+ if (tableid >= countof(keyboard->table))
+ return 0;
+
+ if (key >= HANGUL_KEYBOARD_TABLE_SIZE)
+ return 0;
+
+ ucschar* table = keyboard->table[tableid];
+ if (table == NULL)
+ return 0;
+
+ return table[key];
+}
+
+static void
+hangul_keyboard_set_mapping(HangulKeyboard *keyboard, int tableid, unsigned key, ucschar value)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (tableid >= countof(keyboard->table))
+ return;
+
+ if (key >= HANGUL_KEYBOARD_TABLE_SIZE)
+ return;
+
+ if (keyboard->table[tableid] == NULL) {
+ ucschar* new_table = malloc(sizeof(ucschar) * HANGUL_KEYBOARD_TABLE_SIZE);
+ if (new_table == NULL)
+ return;
+
+ unsigned i;
+ for (i = 0; i < HANGUL_KEYBOARD_TABLE_SIZE; ++i) {
+ new_table[i] = 0;
+ }
+ keyboard->table[tableid] = new_table;
+ }
+
+ ucschar* table = keyboard->table[tableid];
+ table[key] = value;
+}
+
+void
+hangul_keyboard_set_value(HangulKeyboard *keyboard, int key, ucschar value)
+{
+ hangul_keyboard_set_mapping(keyboard, 0, key, value);
+}
+
+int
+hangul_keyboard_get_type(const HangulKeyboard *keyboard)
+{
+ int type = 0;
+ if (keyboard != NULL) {
+ type = keyboard->type;
+ }
+ return type;
+}
+
+void
+hangul_keyboard_set_type(HangulKeyboard *keyboard, int type)
+{
+ if (keyboard != NULL) {
+ keyboard->type = type;
+ }
+}
+
+void
+hangul_keyboard_delete(HangulKeyboard *keyboard)
+{
+ if (keyboard == NULL)
+ return;
+
+ if (keyboard->is_static)
+ return;
+
+ free(keyboard->id);
+ free(keyboard->name);
+
+ unsigned i;
+ for (i = 0; i < countof(keyboard->table); ++i) {
+ if (keyboard->table[i] != NULL) {
+ free(keyboard->table[i]);
+ }
+ }
+
+ for (i = 0; i < countof(keyboard->combination); ++i) {
+ if (keyboard->combination[i] != NULL) {
+ hangul_combination_delete(keyboard->combination[i]);
+ }
+ }
+
+ free(keyboard);
+}
+
+ucschar
+hangul_keyboard_combine(const HangulKeyboard* keyboard,
+ unsigned id, ucschar first, ucschar second)
+{
+ if (keyboard == NULL)
+ return 0;
+
+ if (id >= countof(keyboard->combination))
+ return 0;
+
+ HangulCombination* combination = keyboard->combination[id];
+ ucschar res = hangul_combination_combine(combination, first, second);
+ return res;
+}
+
+static const char*
+attr_lookup(const char** attr, const char* name)
+{
+ if (attr == NULL)
+ return NULL;
+
+ int i;
+ for (i = 0; attr[i] != NULL; i += 2) {
+ if (strcmp(attr[i], name) == 0) {
+ return attr[i + 1];
+ }
+ }
+
+ return NULL;
+}
+
+static unsigned int
+attr_lookup_as_uint(const char** attr, const char* name)
+{
+ const char* valuestr = attr_lookup(attr, name);
+ if (valuestr == NULL)
+ return 0;
+
+ unsigned int value = strtoul(valuestr, NULL, 0);
+ return value;
+}
+
+static void XMLCALL
+on_element_start(void* data, const XML_Char* element, const XML_Char** attr)
+{
+ HangulKeyboardLoadContext* context = (HangulKeyboardLoadContext*)data;
+
+ if (strcmp(element, "hangul-keyboard") == 0) {
+ if (context->keyboard != NULL) {
+ hangul_keyboard_delete(context->keyboard);
+ }
+ context->keyboard = hangul_keyboard_new();
+
+ const char* id = attr_lookup(attr, "id");
+ hangul_keyboard_set_id(context->keyboard, id);
+
+ const char* typestr = attr_lookup(attr, "type");
+ int type = HANGUL_KEYBOARD_TYPE_JAMO;
+ if (strcmp(typestr, "jamo") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JAMO;
+ } else if (strcmp(typestr, "jamo-yet") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JAMO_YET;
+ } else if (strcmp(typestr, "jaso") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JASO;
+ } else if (strcmp(typestr, "jaso-yet") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_JASO_YET;
+ } else if (strcmp(typestr, "romaja") == 0) {
+ type = HANGUL_KEYBOARD_TYPE_ROMAJA;
+ }
+
+ hangul_keyboard_set_type(context->keyboard, type);
+ } else if (strcmp(element, "name") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ const char* lang = attr_lookup(attr, "xml:lang");
+ if (lang == NULL) {
+ context->save_name = true;
+ } else {
+ const char* locale = setlocale(LC_ALL, NULL);
+ size_t n = strlen(lang);
+ if (strncmp(lang, locale, n) == 0) {
+ context->save_name = true;
+ }
+ }
+ context->current_element = "name";
+ } else if (strcmp(element, "map") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ unsigned int id = attr_lookup_as_uint(attr, "id");
+ if (id < countof(context->keyboard->table)) {
+ context->current_id = id;
+ context->current_element = "map";
+ }
+ } else if (strcmp(element, "combination") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ unsigned int id = attr_lookup_as_uint(attr, "id");
+ if (id < countof(context->keyboard->combination)) {
+ if (context->keyboard->combination[id] != NULL) {
+ hangul_combination_delete(context->keyboard->combination[id]);
+ }
+
+ context->current_id = id;
+ context->current_element = "combination";
+ context->keyboard->combination[id] = hangul_combination_new();
+ }
+ } else if (strcmp(element, "item") == 0) {
+ if (context->keyboard == NULL)
+ return;
+
+ unsigned int id = context->current_id;
+ if (strcmp(context->current_element, "map") == 0) {
+ HangulKeyboard* keyboard = context->keyboard;
+ unsigned int key = attr_lookup_as_uint(attr, "key");
+ unsigned int value = attr_lookup_as_uint(attr, "value");
+ hangul_keyboard_set_mapping(keyboard, id, key, value);
+ } else if (strcmp(context->current_element, "combination") == 0) {
+ HangulCombination* combination = context->keyboard->combination[id];
+ unsigned int first = attr_lookup_as_uint(attr, "first");
+ unsigned int second = attr_lookup_as_uint(attr, "second");
+ unsigned int result = attr_lookup_as_uint(attr, "result");
+ hangul_combination_add_item(combination, first, second, result);
+ }
+ } else if (strcmp(element, "include") == 0) {
+ const char* file = attr_lookup(attr, "file");
+ if (file == NULL)
+ return;
+
+ size_t n = strlen(file) + strlen(context->path) + 1;
+ char* path = malloc(n);
+ if (path == NULL)
+ return;
+
+ if (file[0] == '/') {
+ strncpy(path, file, n);
+ } else {
+ char* orig_path = strdup(context->path);
+ char* dir = dirname(orig_path);
+ snprintf(path, n, "%s/%s", dir, file);
+ free(orig_path);
+ }
+
+ hangul_keyboard_parse_file(path, context);
+ free(path);
+ }
+}
+
+static void XMLCALL
+on_element_end(void* data, const XML_Char* element)
+{
+ HangulKeyboardLoadContext* context = (HangulKeyboardLoadContext*)data;
+
+ if (context->keyboard == NULL)
+ return;
+
+ if (strcmp(element, "name") == 0) {
+ context->current_element = "";
+ context->save_name = false;
+ } else if (strcmp(element, "map") == 0) {
+ context->current_id = 0;
+ context->current_element = "";
+ } else if (strcmp(element, "combination") == 0) {
+ unsigned int id = context->current_id;
+ HangulCombination* combination = context->keyboard->combination[id];
+ hangul_combination_sort(combination);
+ context->current_id = 0;
+ context->current_element = "";
+ }
+}
+
+static void XMLCALL
+on_char_data(void* data, const XML_Char* s, int len)
+{
+ HangulKeyboardLoadContext* context = (HangulKeyboardLoadContext*)data;
+
+ if (context->keyboard == NULL)
+ return;
+
+ if (strcmp(context->current_element, "name") == 0) {
+ if (context->save_name) {
+ char buf[1024];
+ if (len >= sizeof(buf))
+ len = sizeof(buf) - 1;
+ memcpy(buf, s, len);
+ buf[len] = '\0';
+ hangul_keyboard_set_name(context->keyboard, buf);
+ }
+ }
+}
+
+static void
+hangul_keyboard_parse_file(const char* path, void* user_data)
+{
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ XML_SetUserData(parser, user_data);
+ XML_SetElementHandler(parser, on_element_start, on_element_end);
+ XML_SetCharacterDataHandler(parser, on_char_data);
+
+ FILE* file = fopen(path, "r");
+ if (file == NULL) {
+ goto done;
+ }
+
+ char buf[8192];
+
+ while (true) {
+ size_t n = fread(buf, 1, sizeof(buf), file);
+ int is_final = feof(file);
+ int res = XML_Parse(parser, buf, n, is_final);
+ if (res == XML_STATUS_ERROR) {
+ goto close;
+ }
+ if (is_final)
+ break;
+ }
+
+close:
+ fclose(file);
+done:
+ XML_ParserFree(parser);
+}
+
+static HangulKeyboard*
+hangul_keyboard_new_from_file(const char* path)
+{
+ HangulKeyboardLoadContext context = { path, NULL, 0, "" };
+
+ hangul_keyboard_parse_file(path, &context);
+
+ return context.keyboard;
+}
+
+static unsigned
+hangul_keyboard_list_load_dir(const char* path)
+{
+ char pattern[PATH_MAX];
+ snprintf(pattern, sizeof(pattern), "%s/*.xml", path);
+
+ glob_t result;
+ int res = glob(pattern, GLOB_ERR, NULL, &result);
+ if (res != 0)
+ return 0;
+
+ size_t i;
+ for (i = 0; i < result.gl_pathc; ++i) {
+ HangulKeyboard* keyboard = hangul_keyboard_new_from_file(result.gl_pathv[i]);
+ if (keyboard == NULL)
+ continue;
+ hangul_keyboard_list_append(keyboard);
+ }
+
+ globfree(&result);
+
+ return hangul_keyboards.n;
+}
+
+static void
+hangul_keyboard_list_clear()
+{
+ size_t i;
+ for (i = 0; i < hangul_keyboards.n; ++i) {
+ hangul_keyboard_delete(hangul_keyboards.keyboards[i]);
+ }
+
+ free(hangul_keyboards.keyboards);
+
+ hangul_keyboards.n = 0;
+ hangul_keyboards.nalloced = 0;
+ hangul_keyboards.keyboards = NULL;
+}
+
+int
+hangul_keyboard_list_init()
+{
+ /* hangul_init을 호출하면 builtin keyboard는 disable되도록 처리한다.
+ * 기본 자판은 외부 파일로 부터 로딩하는 것이 기본 동작이고
+ * builtin 키보드는 하위 호환을 위해 남겨둔다. */
+ hangul_builtin_keyboard_count = 0;
+
+ unsigned n = 0;
+ /* libhangul data dir에서 keyboard 로딩 */
+ n += hangul_keyboard_list_load_dir(LIBHANGUL_KEYBOARD_DIR);
+
+ /* 유저의 개별 키보드 파일 로딩 */
+ char user_data_dir[PATH_MAX];
+ char* xdg_data_home = getenv("XDG_DATA_HOME");
+ if (xdg_data_home == NULL) {
+ char* home_dir = getenv("HOME");
+ snprintf(user_data_dir, sizeof(user_data_dir),
+ "%s/.local/share/libhangul/keyboards", home_dir);
+ } else {
+ snprintf(user_data_dir, sizeof(user_data_dir),
+ "%s/libhangul/keyboards", xdg_data_home);
+ }
+ n += hangul_keyboard_list_load_dir(user_data_dir);
+
+ if (n == 0)
+ return 1;
+
+ return 0;
+}
+
+int
+hangul_keyboard_list_fini()
+{
+ hangul_keyboard_list_clear();
+ hangul_builtin_keyboard_count = countof(hangul_builtin_keyboards);
+ return 0;
+}
+
+static char*
+hangul_builtin_keyboard_list_get_keyboard_id(unsigned index_)
+{
+ if (index_ >= hangul_builtin_keyboard_count)
+ return NULL;
+
+ const HangulKeyboard* keyboard = hangul_builtin_keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->id;
+}
+
+static const char*
+hangul_builtin_keyboard_list_get_keyboard_name(unsigned index_)
+{
+#ifdef ENABLE_NLS
+ static bool isGettextInitialized = false;
+ if (!isGettextInitialized) {
+ isGettextInitialized = true;
+ bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+ }
+#endif
+
+ if (index_ >= hangul_builtin_keyboard_count)
+ return NULL;
+
+ const HangulKeyboard* keyboard = hangul_builtin_keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->name;
+}
+
+static const HangulKeyboard*
+hangul_builtin_keyboard_list_get_keyboard(const char* id)
+{
+ size_t i;
+ for (i = 0; i < hangul_builtin_keyboard_count; ++i) {
+ const HangulKeyboard* keyboard = hangul_builtin_keyboards[i];
+ if (strcmp(id, keyboard->id) == 0) {
+ return keyboard;
+ }
+ }
+ return NULL;
+}
+
+unsigned int
+hangul_keyboard_list_get_count()
+{
+ if (hangul_builtin_keyboard_count > 0)
+ return hangul_builtin_keyboard_count;
+
+ return hangul_keyboards.n;
+}
+
+const char*
+hangul_keyboard_list_get_keyboard_id(unsigned index_)
+{
+ if (hangul_builtin_keyboard_count > 0) {
+ return hangul_builtin_keyboard_list_get_keyboard_id(index_);
+ }
+
+ if (index_ >= hangul_keyboards.n)
+ return NULL;
+
+ HangulKeyboard* keyboard = hangul_keyboards.keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->id;
+}
+
+const char*
+hangul_keyboard_list_get_keyboard_name(unsigned index_)
+{
+ if (hangul_builtin_keyboard_count > 0) {
+ return hangul_builtin_keyboard_list_get_keyboard_name(index_);
+ }
+
+ if (index_ >= hangul_keyboards.n)
+ return NULL;
+
+ HangulKeyboard* keyboard = hangul_keyboards.keyboards[index_];
+ if (keyboard == NULL)
+ return NULL;
+
+ return keyboard->name;
+}
+
+const HangulKeyboard*
+hangul_keyboard_list_get_keyboard(const char* id)
+{
+ if (hangul_builtin_keyboard_count > 0) {
+ return hangul_builtin_keyboard_list_get_keyboard(id);
+ }
+
+ size_t i;
+ for (i = 0; i < hangul_keyboards.n; ++i) {
+ HangulKeyboard* keyboard = hangul_keyboards.keyboards[i];
+ if (strcmp(id, keyboard->id) == 0) {
+ return keyboard;
+ }
+ }
+ return NULL;
+}
+
+static bool
+hangul_keyboard_list_append(HangulKeyboard* keyboard)
+{
+ if (hangul_keyboards.n >= hangul_keyboards.nalloced) {
+ size_t n = hangul_keyboards.nalloced * 2;
+ if (n == 0) {
+ n = 16;
+ }
+ HangulKeyboard** keyboards = hangul_keyboards.keyboards;
+ keyboards = realloc(keyboards, n * sizeof(keyboards[0]));
+ if (keyboards == NULL)
+ return false;
+
+ hangul_keyboards.nalloced = n;
+ hangul_keyboards.keyboards = keyboards;
+ }
+
+ size_t i = hangul_keyboards.n;
+ hangul_keyboards.keyboards[i] = keyboard;
+ hangul_keyboards.n = i + 1;
+
+ return true;
+}