summaryrefslogtreecommitdiff
path: root/hangul
diff options
context:
space:
mode:
authorChoe Hwanjin <choe.hwanjin@gmail.com>2016-03-04 18:10:29 +0900
committerChoe Hwanjin <choe.hwanjin@gmail.com>2016-03-05 11:18:58 +0900
commitacf25460c6ec11259ce5044c1005a4d620c9ae4c (patch)
tree9eecdd84792a6a0bde584db177325b0eaa461014 /hangul
parentbf4981327a5d5985b573a2e594c31b455ec59ba6 (diff)
downloadlibhangul-acf25460c6ec11259ce5044c1005a4d620c9ae4c.tar.gz
xml로 작성된 외부의 한글 자판을 로딩하는 기능 구현
한글 자판 파일의 확장성을 고려하여 자판 파일은 xml로 구성하도록 한다. xml 파서는 expat을 사용한다. xml 번역을 위해서 intltool을 사용하고 autopoint대신 intltool로 gettext을 초기화 한다. 한글 자판 관리는 HangulKeyboardList를 통해서 한다. 한글 자판을 로딩하고 언로딩하기 위해서 hangul_init()/hangul_fini()와 같은 함수를 추가로 도입한다. HangulKeyboard는 HangulCombination과 세트로 관리하는 편이 합리적인 것 같다.
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;
+}