diff options
Diffstat (limited to 'hangul')
-rw-r--r-- | hangul/Makefile.am | 11 | ||||
-rw-r--r-- | hangul/hangul.h | 29 | ||||
-rw-r--r-- | hangul/hangulinputcontext.c | 375 | ||||
-rw-r--r-- | hangul/hangulinternals.h | 31 | ||||
-rw-r--r-- | hangul/hangulkeyboard.c | 950 |
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; +} |