81 lines
2.9 KiB
C
81 lines
2.9 KiB
C
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#include "native.h"
|
|
#include <dlfcn.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
|
|
// Enchant 2's public ABI uses opaque broker/dictionary pointers. Resolve that
|
|
// ABI at runtime so the dynamic linker never requires Enchant to start Fenrir.
|
|
typedef struct _EnchantBroker EnchantBroker;
|
|
typedef struct _EnchantDict EnchantDict;
|
|
|
|
struct SpellDictionary {
|
|
void *library;
|
|
EnchantBroker *broker;
|
|
EnchantDict *dictionary;
|
|
EnchantBroker *(*broker_init)(void);
|
|
void (*broker_free)(EnchantBroker *broker);
|
|
EnchantDict *(*request_dict)(EnchantBroker *broker, const char *language);
|
|
void (*free_dict)(EnchantBroker *broker, EnchantDict *dictionary);
|
|
int (*check)(EnchantDict *dictionary, const char *word, ssize_t length);
|
|
char **(*suggest)(EnchantDict *dictionary, const char *word, ssize_t length, size_t *count);
|
|
void (*free_suggestions)(EnchantDict *dictionary, char **suggestions);
|
|
};
|
|
|
|
void spell_dictionary_close(SpellDictionary *context) {
|
|
if (!context) return;
|
|
if (context->dictionary) context->free_dict(context->broker, context->dictionary);
|
|
if (context->broker) context->broker_free(context->broker);
|
|
if (context->library) dlclose(context->library);
|
|
free(context);
|
|
}
|
|
|
|
SpellDictionary *spell_dictionary_open(const char *language, int *status) {
|
|
*status = 3;
|
|
SpellDictionary *context = calloc(1, sizeof(*context));
|
|
if (!context) return NULL;
|
|
context->library = dlopen("libenchant-2.so.2", RTLD_NOW | RTLD_LOCAL);
|
|
if (!context->library) {
|
|
*status = 1;
|
|
goto failed;
|
|
}
|
|
*status = 2;
|
|
#define LOAD(field, symbol) \
|
|
context->field = dlsym(context->library, symbol); \
|
|
if (!context->field) goto failed
|
|
LOAD(broker_init, "enchant_broker_init");
|
|
LOAD(broker_free, "enchant_broker_free");
|
|
LOAD(request_dict, "enchant_broker_request_dict");
|
|
LOAD(free_dict, "enchant_broker_free_dict");
|
|
LOAD(check, "enchant_dict_check");
|
|
LOAD(suggest, "enchant_dict_suggest");
|
|
LOAD(free_suggestions, "enchant_dict_free_string_list");
|
|
#undef LOAD
|
|
*status = 3;
|
|
context->broker = context->broker_init();
|
|
if (!context->broker) goto failed;
|
|
*status = 4;
|
|
context->dictionary = context->request_dict(context->broker, language);
|
|
if (!context->dictionary) goto failed;
|
|
*status = 0;
|
|
return context;
|
|
failed:
|
|
spell_dictionary_close(context);
|
|
return NULL;
|
|
}
|
|
|
|
int spell_dictionary_check(SpellDictionary *context, const char *word, size_t length) {
|
|
return context->check(context->dictionary, word, (ssize_t)length);
|
|
}
|
|
|
|
char **spell_dictionary_suggest(SpellDictionary *context, const char *word,
|
|
size_t length, size_t *count) {
|
|
return context->suggest(context->dictionary, word, (ssize_t)length, count);
|
|
}
|
|
|
|
void spell_dictionary_free_suggestions(SpellDictionary *context, char **suggestions) {
|
|
context->free_suggestions(context->dictionary, suggestions);
|
|
}
|