diff options
Diffstat (limited to 'hangul/hangulkeyboard.c')
-rw-r--r-- | hangul/hangulkeyboard.c | 950 |
1 files changed, 950 insertions, 0 deletions
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; +} |