#include "platform.h" #include "stk.h" #include #include #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); }