Files

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);
}