449 lines
9.8 KiB
C
449 lines
9.8 KiB
C
#include "platform.h"
|
|
#include "stk.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define STK_MOD_FUNC_NAME_BUFFER 64
|
|
|
|
typedef int (*stk_init_mod_func)(void);
|
|
typedef void (*stk_shutdown_mod_func)(void);
|
|
|
|
typedef struct {
|
|
unsigned char major;
|
|
unsigned char minor;
|
|
unsigned char patch;
|
|
char op;
|
|
} stk_version_t;
|
|
|
|
typedef struct {
|
|
char id[STK_MOD_ID_BUFFER];
|
|
char version[STK_MOD_VERSION_BUFFER];
|
|
} stk_dep_t;
|
|
|
|
typedef struct {
|
|
void *handle;
|
|
stk_init_mod_func init;
|
|
stk_shutdown_mod_func shutdown;
|
|
char id[STK_MOD_ID_BUFFER];
|
|
char name[STK_MOD_NAME_BUFFER];
|
|
char version[STK_MOD_VERSION_BUFFER];
|
|
char desc[STK_MOD_DESC_BUFFER];
|
|
stk_dep_t *deps;
|
|
size_t dep_count;
|
|
} stk_mod_t;
|
|
|
|
void *platform_load_library(const char *path);
|
|
void platform_unload_library(void *handle);
|
|
void *platform_get_symbol(void *handle, const char *symbol);
|
|
|
|
stk_mod_t *stk_modules = NULL;
|
|
|
|
extern unsigned char stk_flags;
|
|
|
|
static char stk_mod_init_name[STK_MOD_FUNC_NAME_BUFFER] = "stk_mod_init";
|
|
static char stk_mod_shutdown_name[STK_MOD_FUNC_NAME_BUFFER] =
|
|
"stk_mod_shutdown";
|
|
static char stk_mod_name_fn[STK_MOD_FUNC_NAME_BUFFER] = "stk_mod_name";
|
|
static char stk_mod_version_fn[STK_MOD_FUNC_NAME_BUFFER] = "stk_mod_version";
|
|
static char stk_mod_description_fn[STK_MOD_FUNC_NAME_BUFFER] =
|
|
"stk_mod_description";
|
|
static char stk_mod_dependencies_fn[STK_MOD_FUNC_NAME_BUFFER] =
|
|
"stk_mod_dependencies";
|
|
|
|
size_t module_count = 0;
|
|
|
|
static stk_version_t stk_parse_version(const char *str)
|
|
{
|
|
stk_version_t v;
|
|
v.major = 0;
|
|
v.minor = 0;
|
|
v.patch = 0;
|
|
v.op = '>';
|
|
|
|
if (!str || !*str)
|
|
return v;
|
|
|
|
if (*str == '=' && *(str + 1) != '=') {
|
|
v.op = '=';
|
|
str++;
|
|
} else if (*str == '>' && *(str + 1) == '=') {
|
|
v.op = '>';
|
|
str += 2;
|
|
} else if (*str == '^') {
|
|
v.op = '^';
|
|
str++;
|
|
}
|
|
|
|
v.major = (unsigned char)strtol(str, (char **)&str, 10);
|
|
if (*str == '.')
|
|
str++;
|
|
v.minor = (unsigned char)strtol(str, (char **)&str, 10);
|
|
if (*str == '.')
|
|
str++;
|
|
v.patch = (unsigned char)strtol(str, NULL, 10);
|
|
|
|
return v;
|
|
}
|
|
|
|
static int stk_compare_version(stk_version_t a, stk_version_t b)
|
|
{
|
|
if (a.major != b.major)
|
|
return a.major - b.major;
|
|
if (a.minor != b.minor)
|
|
return a.minor - b.minor;
|
|
return a.patch - b.patch;
|
|
}
|
|
|
|
static int stk_validate_constraint(const char *constraint, const char *loaded)
|
|
{
|
|
stk_version_t req = stk_parse_version(constraint);
|
|
stk_version_t have = stk_parse_version(loaded);
|
|
int cmp = stk_compare_version(have, req);
|
|
|
|
switch (req.op) {
|
|
case '=':
|
|
return cmp == 0;
|
|
case '^':
|
|
return have.major == req.major && cmp >= 0;
|
|
default:
|
|
return cmp >= 0;
|
|
}
|
|
}
|
|
|
|
size_t stk_module_count(void) { return module_count; }
|
|
|
|
void extract_module_id(const char *path, char *out_id)
|
|
{
|
|
char *dot;
|
|
const char *basename = strrchr(path, STK_PATH_SEP);
|
|
|
|
basename = (basename) ? basename + 1 : path;
|
|
|
|
strncpy(out_id, basename, STK_MOD_ID_BUFFER - 1);
|
|
out_id[STK_MOD_ID_BUFFER - 1] = '\0';
|
|
|
|
dot = strrchr(out_id, '.');
|
|
if (dot)
|
|
*dot = '\0';
|
|
}
|
|
|
|
unsigned char is_valid_module_file(const char *filename)
|
|
{
|
|
const char *ext;
|
|
size_t name_len;
|
|
|
|
if (!filename)
|
|
return 0;
|
|
|
|
name_len = strlen(filename);
|
|
|
|
if (name_len <= STK_MODULE_EXT_LEN)
|
|
return 0;
|
|
|
|
ext = filename + (name_len - STK_MODULE_EXT_LEN);
|
|
return strcmp(ext, STK_MODULE_EXT) == 0;
|
|
}
|
|
|
|
int is_mod_loaded(const char *module_name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < module_count; i++)
|
|
if (strncmp(stk_modules[i].id, module_name,
|
|
STK_MOD_ID_BUFFER) == 0)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static unsigned char stk_validate_dependencies(size_t count)
|
|
{
|
|
size_t i, d;
|
|
int found;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (stk_modules[i].dep_count == 0)
|
|
continue;
|
|
|
|
for (d = 0; d < stk_modules[i].dep_count; d++) {
|
|
found = is_mod_loaded(stk_modules[i].deps[d].id);
|
|
if (found < 0)
|
|
return STK_MOD_DEP_NOT_FOUND_ERROR;
|
|
|
|
if (!stk_modules[i].deps[d].version[0])
|
|
continue;
|
|
|
|
if (!stk_validate_constraint(
|
|
stk_modules[i].deps[d].version,
|
|
stk_modules[found].version))
|
|
return STK_MOD_DEP_VERSION_MISMATCH_ERROR;
|
|
}
|
|
}
|
|
|
|
return STK_MOD_INIT_SUCCESS;
|
|
}
|
|
|
|
unsigned char stk_module_load(const char *path, int index)
|
|
{
|
|
void *handle;
|
|
char module_id[STK_MOD_ID_BUFFER];
|
|
size_t len;
|
|
union {
|
|
void *obj;
|
|
stk_init_mod_func init_func;
|
|
stk_shutdown_mod_func shutdown_func;
|
|
const char *(*meta_func)(void);
|
|
const char *(*(*deps_func)(void))[2];
|
|
} u;
|
|
const char *meta_str;
|
|
const char *(*deps)[2];
|
|
size_t dep_count;
|
|
stk_dep_t *dep_arr;
|
|
|
|
handle = platform_load_library(path);
|
|
if (!handle)
|
|
return STK_MOD_LIBRARY_LOAD_ERROR;
|
|
|
|
u.obj = platform_get_symbol(handle, stk_mod_init_name);
|
|
if (!u.obj) {
|
|
platform_unload_library(handle);
|
|
return STK_MOD_SYMBOL_NOT_FOUND_ERROR;
|
|
}
|
|
stk_modules[index].init = u.init_func;
|
|
|
|
u.obj = platform_get_symbol(handle, stk_mod_shutdown_name);
|
|
if (!u.obj) {
|
|
platform_unload_library(handle);
|
|
return STK_MOD_SYMBOL_NOT_FOUND_ERROR;
|
|
}
|
|
stk_modules[index].shutdown = u.shutdown_func;
|
|
|
|
extract_module_id(path, module_id);
|
|
|
|
if (stk_modules[index].init() != STK_MOD_INIT_SUCCESS) {
|
|
platform_unload_library(handle);
|
|
return STK_MOD_INIT_FAILURE;
|
|
}
|
|
|
|
stk_modules[index].handle = handle;
|
|
|
|
len = strlen(module_id);
|
|
if (len >= STK_MOD_ID_BUFFER)
|
|
len = STK_MOD_ID_BUFFER - 1;
|
|
memcpy(stk_modules[index].id, module_id, len);
|
|
stk_modules[index].id[len] = '\0';
|
|
|
|
stk_modules[index].name[0] = '\0';
|
|
u.obj = platform_get_symbol(handle, stk_mod_name_fn);
|
|
if (u.obj) {
|
|
meta_str = u.meta_func();
|
|
if (meta_str) {
|
|
strncpy(stk_modules[index].name, meta_str,
|
|
STK_MOD_NAME_BUFFER - 1);
|
|
stk_modules[index].name[STK_MOD_NAME_BUFFER - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
stk_modules[index].version[0] = '\0';
|
|
u.obj = platform_get_symbol(handle, stk_mod_version_fn);
|
|
if (u.obj) {
|
|
meta_str = u.meta_func();
|
|
if (meta_str) {
|
|
strncpy(stk_modules[index].version, meta_str,
|
|
STK_MOD_VERSION_BUFFER - 1);
|
|
stk_modules[index].version[STK_MOD_VERSION_BUFFER - 1] =
|
|
'\0';
|
|
}
|
|
}
|
|
|
|
stk_modules[index].desc[0] = '\0';
|
|
u.obj = platform_get_symbol(handle, stk_mod_description_fn);
|
|
if (u.obj) {
|
|
meta_str = u.meta_func();
|
|
if (meta_str) {
|
|
strncpy(stk_modules[index].desc, meta_str,
|
|
STK_MOD_DESC_BUFFER - 1);
|
|
stk_modules[index].desc[STK_MOD_DESC_BUFFER - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
stk_modules[index].deps = NULL;
|
|
stk_modules[index].dep_count = 0;
|
|
u.obj = platform_get_symbol(handle, stk_mod_dependencies_fn);
|
|
if (!u.obj)
|
|
goto skip_deps;
|
|
|
|
deps = u.deps_func();
|
|
if (!deps)
|
|
goto skip_deps;
|
|
|
|
dep_count = 0;
|
|
while (deps[dep_count][0] != NULL)
|
|
dep_count++;
|
|
|
|
if (dep_count == 0)
|
|
goto skip_deps;
|
|
|
|
dep_arr = malloc(dep_count * sizeof(stk_dep_t));
|
|
if (!dep_arr)
|
|
goto skip_deps;
|
|
|
|
{
|
|
size_t d;
|
|
for (d = 0; d < dep_count; d++) {
|
|
strncpy(dep_arr[d].id, deps[d][0],
|
|
STK_MOD_ID_BUFFER - 1);
|
|
dep_arr[d].id[STK_MOD_ID_BUFFER - 1] = '\0';
|
|
strncpy(dep_arr[d].version, deps[d][1],
|
|
STK_MOD_VERSION_BUFFER - 1);
|
|
dep_arr[d].version[STK_MOD_VERSION_BUFFER - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
stk_modules[index].deps = dep_arr;
|
|
stk_modules[index].dep_count = dep_count;
|
|
|
|
skip_deps:
|
|
return STK_MOD_INIT_SUCCESS;
|
|
}
|
|
|
|
unsigned char stk_module_load_init(const char *path, int index)
|
|
{
|
|
int result;
|
|
result = stk_module_load(path, index);
|
|
|
|
if (result == STK_MOD_INIT_SUCCESS)
|
|
++module_count;
|
|
|
|
return result;
|
|
}
|
|
|
|
void stk_module_unload(size_t index)
|
|
{
|
|
stk_modules[index].shutdown();
|
|
platform_unload_library(stk_modules[index].handle);
|
|
|
|
stk_modules[index].handle = NULL;
|
|
stk_modules[index].init = NULL;
|
|
stk_modules[index].shutdown = NULL;
|
|
stk_modules[index].id[0] = '\0';
|
|
stk_modules[index].name[0] = '\0';
|
|
stk_modules[index].version[0] = '\0';
|
|
stk_modules[index].desc[0] = '\0';
|
|
|
|
if (stk_modules[index].deps) {
|
|
free(stk_modules[index].deps);
|
|
stk_modules[index].deps = NULL;
|
|
}
|
|
stk_modules[index].dep_count = 0;
|
|
}
|
|
|
|
void stk_module_free_memory(void)
|
|
{
|
|
if (stk_modules) {
|
|
size_t i;
|
|
for (i = 0; i < module_count; i++) {
|
|
if (stk_modules[i].deps)
|
|
free(stk_modules[i].deps);
|
|
}
|
|
free(stk_modules);
|
|
stk_modules = NULL;
|
|
}
|
|
module_count = 0;
|
|
}
|
|
|
|
unsigned char stk_module_init_memory(size_t capacity)
|
|
{
|
|
stk_modules = malloc(capacity * sizeof(stk_mod_t));
|
|
if (!stk_modules)
|
|
return STK_INIT_MEMORY_ERROR;
|
|
|
|
return STK_INIT_SUCCESS;
|
|
}
|
|
|
|
unsigned char stk_module_realloc_memory(size_t new_capacity)
|
|
{
|
|
stk_mod_t *new_modules;
|
|
size_t i, copy_count;
|
|
|
|
if (new_capacity == 0) {
|
|
stk_module_free_memory();
|
|
return 0;
|
|
}
|
|
|
|
new_modules = malloc(new_capacity * sizeof(stk_mod_t));
|
|
if (!new_modules)
|
|
return STK_MOD_REALLOC_FAILURE;
|
|
|
|
copy_count =
|
|
(module_count < new_capacity) ? module_count : new_capacity;
|
|
|
|
for (i = 0; i < copy_count; i++)
|
|
new_modules[i] = stk_modules[i];
|
|
|
|
for (i = copy_count; i < new_capacity; i++) {
|
|
new_modules[i].handle = NULL;
|
|
new_modules[i].init = NULL;
|
|
new_modules[i].shutdown = NULL;
|
|
new_modules[i].id[0] = '\0';
|
|
new_modules[i].name[0] = '\0';
|
|
new_modules[i].version[0] = '\0';
|
|
new_modules[i].desc[0] = '\0';
|
|
new_modules[i].deps = NULL;
|
|
new_modules[i].dep_count = 0;
|
|
}
|
|
|
|
free(stk_modules);
|
|
stk_modules = new_modules;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void stk_module_unload_all(void)
|
|
{
|
|
size_t i;
|
|
for (i = module_count; i > 0; --i)
|
|
stk_module_unload(i - 1);
|
|
|
|
stk_module_free_memory();
|
|
}
|
|
|
|
static void stk_set_fn_name(char *dst, const char *name)
|
|
{
|
|
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
|
|
return;
|
|
|
|
strncpy(dst, name, STK_MOD_FUNC_NAME_BUFFER - 1);
|
|
dst[STK_MOD_FUNC_NAME_BUFFER - 1] = '\0';
|
|
}
|
|
|
|
void stk_set_module_init_fn(const char *name)
|
|
{
|
|
stk_set_fn_name(stk_mod_init_name, name);
|
|
}
|
|
|
|
void stk_set_module_shutdown_fn(const char *name)
|
|
{
|
|
stk_set_fn_name(stk_mod_shutdown_name, name);
|
|
}
|
|
|
|
void stk_set_module_name_fn(const char *name)
|
|
{
|
|
stk_set_fn_name(stk_mod_name_fn, name);
|
|
}
|
|
|
|
void stk_set_module_version_fn(const char *name)
|
|
{
|
|
stk_set_fn_name(stk_mod_version_fn, name);
|
|
}
|
|
|
|
void stk_set_module_description_fn(const char *name)
|
|
{
|
|
stk_set_fn_name(stk_mod_description_fn, name);
|
|
}
|
|
|
|
void stk_set_module_dependencies_fn(const char *name)
|
|
{
|
|
stk_set_fn_name(stk_mod_dependencies_fn, name);
|
|
}
|