Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f7f216c92 | |||
| 70d50dda92 | |||
| 70e9ec2fc3 | |||
| 110b7ffca8 | |||
| 26fb19a7f5 | |||
| bcb1795218 | |||
| fb0d8adb8f | |||
| 2c4d27f915 | |||
| 96fb957991 | |||
| 0cbee45ad2 | |||
| 142a61a843 | |||
| f83f2051ef |
+73
-1
@@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.1.1] - 2026-02-15
|
||||
|
||||
### Fixed
|
||||
- **Logging**: Corrected log level severity order in enum
|
||||
- Reversed order so DEBUG (0) < INFO (1) < WARN (2) < ERROR (3)
|
||||
- Fixes filtering logic where ERROR/WARN were incorrectly blocked
|
||||
- Default INFO level now properly shows INFO, WARN, and ERROR while filtering DEBUG
|
||||
|
||||
## [0.1.0] - 2026-02-15
|
||||
|
||||
### Fixed
|
||||
- **C89 Compliance**: Removed stdint.h dependency (C99 feature)
|
||||
- Replaced all uint8_t with unsigned char throughout codebase
|
||||
- Ensures strict C89 compliance for maximum portability
|
||||
|
||||
### Added
|
||||
- **Flags system**: Centralized bitfield for boolean state and settings
|
||||
- Replaces stk_initialized with stk_flags for efficient memory usage
|
||||
- Single byte packs all boolean flags (STK_FLAG_INITIALIZED, STK_FLAG_LOGGING_ENABLED)
|
||||
- Runtime-changeable logging control via stk_set_logging_enabled()
|
||||
|
||||
- **Enhanced logging system**: Complete rewrite with modern features
|
||||
- Log levels: ERROR, WARN, INFO, DEBUG with runtime filtering
|
||||
- Timestamps: yyyy-mm-dd HH:MM:SS.mmm format (platform-specific implementation)
|
||||
- Configurable log output stream (stk_set_log_output)
|
||||
- Configurable log prefix (stk_set_log_prefix, defaults to "stk")
|
||||
- Configurable minimum log level (stk_set_log_level, defaults to INFO)
|
||||
- Platform abstraction for timestamps (GetLocalTime on Windows, gettimeofday on POSIX)
|
||||
|
||||
### Changed
|
||||
- **BREAKING**: stk_log() signature changed from stk_log(FILE *fp, ...) to stk_log(stk_log_level_t level, ...)
|
||||
- **BREAKING**: All uint8_t types replaced with unsigned char
|
||||
- All internal STK messages now use appropriate log levels
|
||||
- Setting log output to NULL disabls logging
|
||||
|
||||
### Notes
|
||||
- This release completes Phase 1 logging improvements
|
||||
- Dependency management still in progress for Phase 1 completion
|
||||
|
||||
## [0.0.4] - 2026-02-11
|
||||
|
||||
### Fixed
|
||||
- **Linux**: Fixed segfault from invalid module indices during extremely rapid file changes
|
||||
- Added validation check to skip stale UNLOAD/RELOAD events for already-unloaded modules
|
||||
- Prevents is_mod_loaded() returning -1 from being used as array index (SIZE_MAX)
|
||||
- Fixed event count mismatch where loops would run more iterations than valid indices populated
|
||||
- Completes the Linux hot-reload stability fixes from v0.0.2
|
||||
|
||||
## [0.0.3] - 2026-02-10
|
||||
|
||||
### Fixed
|
||||
- **Compilation**: Fixed GCC `-Wrestrict` warning in Linux directory watching code by replacing `strncpy` with `memmove` for overlapping memory operations
|
||||
- Ensures clean compilation without warnings while maintaining identical runtime behavior
|
||||
- Uses semantically correct function for moving memory within the same buffer
|
||||
|
||||
## [0.0.2] - 2026-02-09
|
||||
|
||||
### Fixed
|
||||
- **Linux**: Fixed segfaults during rapid module reloads when file changes are detected in quick succession
|
||||
- Enabled file readiness checks on Linux (previously only used on Windows/BSD) to prevent loading partially-written shared libraries
|
||||
- Fixed inotify event deduplication to actually remove duplicate events instead of just marking them
|
||||
- Reordered reload operations to only unload old module after successfully copying new version, preventing invalid state when copy fails
|
||||
- **All platforms**: Improved reload safety by deferring module unload until after successful file copy
|
||||
|
||||
### Changed
|
||||
- Made `is_file_ready()` check available on all Unix platforms (was previously excluded on Linux)
|
||||
|
||||
## [0.0.1] - 2026-02-01
|
||||
|
||||
### Added
|
||||
@@ -25,5 +92,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Dependency management and versioning not yet implemented
|
||||
- API is unstable and subject to change in future releases
|
||||
|
||||
[Unreleased]: https://github.com/anth64/stk/compare/v0.0.1...HEAD
|
||||
[Unreleased]: https://github.com/anth64/stk/compare/v0.1.1...HEAD
|
||||
[0.1.1]: https://github.com/anth64/stk/releases/tag/v0.1.1
|
||||
[0.1.0]: https://github.com/anth64/stk/releases/tag/v0.1.0
|
||||
[0.0.4]: https://github.com/anth64/stk/releases/tag/v0.0.4
|
||||
[0.0.3]: https://github.com/anth64/stk/releases/tag/v0.0.3
|
||||
[0.0.2]: https://github.com/anth64/stk/releases/tag/v0.0.2
|
||||
[0.0.1]: https://github.com/anth64/stk/releases/tag/v0.0.1
|
||||
|
||||
@@ -156,7 +156,7 @@ stk_init();
|
||||
### API Reference
|
||||
|
||||
#### Initialization
|
||||
- `uint8_t stk_init(void)` - Initialize stk, returns `STK_INIT_SUCCESS` on success
|
||||
- `unsigned char stk_init(void)` - Initialize stk, returns `STK_INIT_SUCCESS` on success
|
||||
- `void stk_shutdown(void)` - Shutdown and cleanup all modules
|
||||
|
||||
#### Runtime
|
||||
@@ -169,21 +169,31 @@ stk_init();
|
||||
- `void stk_set_module_init_fn(const char *name)` - Set module init function name
|
||||
- `void stk_set_module_shutdown_fn(const char *name)` - Set module shutdown function name
|
||||
|
||||
#### Logging
|
||||
- `void stk_set_logging_enabled(unsigned char enabled)` - Enable/disable all logging
|
||||
- `unsigned char stk_is_logging_enabled(void)` - Query logging state
|
||||
- `void stk_set_log_output(FILE *fp)` - Set log output stream (default: stdout, NULL disables)
|
||||
- `void stk_set_log_prefix(const char *prefix)` - Set log prefix (default: "stk")
|
||||
- `void stk_set_log_level(stk_log_level_t level)` - Set minimum log level (default: INFO)
|
||||
|
||||
**Log Levels:** `STK_LOG_ERROR`, `STK_LOG_WARN`, `STK_LOG_INFO`, `STK_LOG_DEBUG`
|
||||
|
||||
---
|
||||
|
||||
## Project Status
|
||||
|
||||
**Current Version:** 0.0.1 (Pre-release)
|
||||
**Current Version:** 0.1.1 (Pre-release)
|
||||
|
||||
This is an early release proving the core hot-reload foundation. Phase 1 is still in progress.
|
||||
This release brings C89 compliance fixes and a complete logging system rewrite with levels, timestamps, and runtime configuration.
|
||||
|
||||
### What Works
|
||||
- Cross-platform module loading and hot-reloading
|
||||
- File watching (inotify/kqueue/FindFirstFile)
|
||||
- Basic error handling
|
||||
- Robust hot-reload even during extremely rapid file changes
|
||||
- Enhanced logging with levels, timestamps, and filtering
|
||||
- Runtime-configurable logging behavior
|
||||
|
||||
### In Progress (Phase 1)
|
||||
- Complete logging system (log levels, verbosity, output configuration)
|
||||
- Module metadata (name, version, description)
|
||||
- Dependency management and versioning
|
||||
|
||||
|
||||
+8
-2
@@ -2,10 +2,10 @@
|
||||
#define STK_H
|
||||
|
||||
#include "stk_version.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Buffers */
|
||||
#define STK_LOG_PREFIX_BUFFER 64
|
||||
#define STK_MOD_DIR_BUFFER 256
|
||||
#define STK_MOD_ID_BUFFER 64
|
||||
#define STK_PATH_MAX 256
|
||||
@@ -31,6 +31,10 @@
|
||||
#define STK_PLATFORM_REMOVE_DIR_ERROR 3
|
||||
#define STK_PLATFORM_REMOVE_FILE_ERROR 4
|
||||
|
||||
/* Settings flags */
|
||||
#define STK_FLAG_INITIALIZED 0x01
|
||||
#define STK_FLAG_LOGGING_ENABLED 0x02
|
||||
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
#define STK_EVENT_BUFFER 4096
|
||||
#endif
|
||||
@@ -55,7 +59,7 @@ typedef enum {
|
||||
STK_MOD_RELOAD
|
||||
} stk_module_event_t;
|
||||
|
||||
uint8_t stk_init(void);
|
||||
unsigned char stk_init(void);
|
||||
void stk_shutdown(void);
|
||||
size_t stk_module_count(void);
|
||||
size_t stk_poll(void);
|
||||
@@ -63,6 +67,8 @@ void stk_set_mod_dir(const char *path);
|
||||
void stk_set_tmp_dir_name(const char *name);
|
||||
void stk_set_module_init_fn(const char *name);
|
||||
void stk_set_module_shutdown_fn(const char *name);
|
||||
void stk_set_logging_enabled(unsigned char enabled);
|
||||
unsigned char stk_is_logging_enabled(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+12
-1
@@ -7,7 +7,18 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void stk_log(FILE *fp, const char *fmt, ...);
|
||||
typedef enum {
|
||||
STK_LOG_DEBUG,
|
||||
STK_LOG_INFO,
|
||||
STK_LOG_WARN,
|
||||
STK_LOG_ERROR
|
||||
} stk_log_level_t;
|
||||
|
||||
void stk_set_log_output(FILE *fp);
|
||||
void stk_set_log_prefix(const char *prefix);
|
||||
void stk_set_log_level(stk_log_level_t min_level);
|
||||
|
||||
void stk_log(stk_log_level_t level, const char *fmt, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define STK_VERSION_H
|
||||
|
||||
#define STK_VERSION_MAJOR 0
|
||||
#define STK_VERSION_MINOR 0
|
||||
#define STK_VERSION_MINOR 1
|
||||
#define STK_VERSION_PATCH 1
|
||||
|
||||
#define STK_STRINGIFY_HELPER(x) #x
|
||||
|
||||
+8
-9
@@ -1,6 +1,5 @@
|
||||
#include "platform.h"
|
||||
#include "stk.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -18,7 +17,7 @@ void **stk_handles = NULL;
|
||||
stk_init_mod_func *stk_inits = NULL;
|
||||
stk_shutdown_mod_func *stk_shutdowns = NULL;
|
||||
|
||||
extern uint8_t stk_initialized;
|
||||
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] =
|
||||
@@ -43,7 +42,7 @@ void extract_module_id(const char *path, char *out_id)
|
||||
*dot = '\0';
|
||||
}
|
||||
|
||||
uint8_t is_valid_module_file(const char *filename)
|
||||
unsigned char is_valid_module_file(const char *filename)
|
||||
{
|
||||
const char *ext;
|
||||
size_t name_len;
|
||||
@@ -72,7 +71,7 @@ int is_mod_loaded(const char *module_name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t stk_module_load(const char *path, int index)
|
||||
unsigned char stk_module_load(const char *path, int index)
|
||||
{
|
||||
void *handle;
|
||||
stk_init_mod_func init_func;
|
||||
@@ -121,7 +120,7 @@ uint8_t stk_module_load(const char *path, int index)
|
||||
return STK_MOD_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t stk_module_load_init(const char *path, int index)
|
||||
unsigned char stk_module_load_init(const char *path, int index)
|
||||
{
|
||||
int result;
|
||||
result = stk_module_load(path, index);
|
||||
@@ -155,7 +154,7 @@ void stk_module_free_memory(void)
|
||||
stk_shutdowns = NULL;
|
||||
}
|
||||
|
||||
uint8_t stk_module_init_memory(size_t capacity)
|
||||
unsigned char stk_module_init_memory(size_t capacity)
|
||||
{
|
||||
stk_module_ids = malloc(capacity * sizeof(*stk_module_ids));
|
||||
stk_handles = malloc(capacity * sizeof(void *));
|
||||
@@ -170,7 +169,7 @@ uint8_t stk_module_init_memory(size_t capacity)
|
||||
return STK_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t stk_module_realloc_memory(size_t new_capacity)
|
||||
unsigned char stk_module_realloc_memory(size_t new_capacity)
|
||||
{
|
||||
char (*new_module_ids)[STK_MOD_ID_BUFFER] = NULL;
|
||||
void **new_handles = NULL;
|
||||
@@ -254,7 +253,7 @@ void stk_module_unload_all(void)
|
||||
|
||||
void stk_set_module_init_fn(const char *name)
|
||||
{
|
||||
if (!name || stk_initialized)
|
||||
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
|
||||
return;
|
||||
|
||||
strncpy(stk_mod_init_name, name, STK_MOD_FUNC_NAME_BUFFER - 1);
|
||||
@@ -263,7 +262,7 @@ void stk_set_module_init_fn(const char *name)
|
||||
|
||||
void stk_set_module_shutdown_fn(const char *name)
|
||||
{
|
||||
if (!name || stk_initialized)
|
||||
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
|
||||
return;
|
||||
|
||||
strncpy(stk_mod_shutdown_name, name, STK_MOD_FUNC_NAME_BUFFER - 1);
|
||||
|
||||
+143
-115
@@ -1,39 +1,34 @@
|
||||
#include "stk.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
int is_mod_loaded(const char *module_name);
|
||||
uint8_t is_valid_module_file(const char *filename);
|
||||
unsigned char is_valid_module_file(const char *filename);
|
||||
void extract_module_id(const char *path, char *out_id);
|
||||
|
||||
#ifndef __linux__
|
||||
static uint8_t is_file_ready(const char *dir_path, const char *filename)
|
||||
static unsigned char is_file_ready(const char *dir_path, const char *filename)
|
||||
{
|
||||
char full_path[STK_PATH_MAX_OS];
|
||||
#ifdef _WIN32
|
||||
@@ -80,6 +75,7 @@ static uint8_t is_file_ready(const char *dir_path, const char *filename)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __linux__
|
||||
typedef struct {
|
||||
char filename[STK_PATH_MAX];
|
||||
#ifdef _WIN32
|
||||
@@ -108,7 +104,7 @@ typedef struct {
|
||||
} platform_watch_context_t;
|
||||
#endif
|
||||
|
||||
uint8_t platform_mkdir(const char *path)
|
||||
unsigned char platform_mkdir(const char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return CreateDirectoryA(path, NULL) ? STK_PLATFORM_OPERATION_SUCCESS
|
||||
@@ -130,7 +126,7 @@ int platform_remove_file(const char *path)
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t platform_copy_file(const char *from, const char *to)
|
||||
unsigned char platform_copy_file(const char *from, const char *to)
|
||||
{
|
||||
char buf[STK_PATH_MAX_OS];
|
||||
int ret = STK_PLATFORM_FILE_COPY_ERROR;
|
||||
@@ -146,7 +142,6 @@ uint8_t platform_copy_file(const char *from, const char *to)
|
||||
FILE *src = NULL, *dst = NULL;
|
||||
char tmp_path[STK_PATH_MAX_OS];
|
||||
size_t n;
|
||||
#ifndef __linux__
|
||||
char dir_path[STK_PATH_MAX_OS];
|
||||
const char *filename;
|
||||
|
||||
@@ -163,7 +158,6 @@ uint8_t platform_copy_file(const char *from, const char *to)
|
||||
|
||||
if (!is_file_ready(dir_path, filename))
|
||||
goto done;
|
||||
#endif
|
||||
|
||||
sprintf(tmp_path, "%s.tmp", to);
|
||||
|
||||
@@ -205,7 +199,7 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t platform_remove_dir(const char *path)
|
||||
unsigned char platform_remove_dir(const char *path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATAA fd;
|
||||
@@ -286,128 +280,127 @@ void *platform_get_symbol(void *h, const char *s)
|
||||
#endif
|
||||
}
|
||||
|
||||
char (*platform_directory_init_scan(const char *dir_path,
|
||||
size_t *out_count))[STK_PATH_MAX]
|
||||
{
|
||||
size_t count = 0, i = 0, name_len;
|
||||
char(*list)[STK_PATH_MAX] = NULL;
|
||||
char (*platform_directory_init_scan(const char *dir_path, size_t *out_count))
|
||||
[STK_PATH_MAX] {
|
||||
size_t count = 0, i = 0, name_len;
|
||||
char (*list)[STK_PATH_MAX] = NULL;
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATAA fd;
|
||||
HANDLE h;
|
||||
char s[STK_PATH_MAX_OS];
|
||||
WIN32_FIND_DATAA fd;
|
||||
HANDLE h;
|
||||
char s[STK_PATH_MAX_OS];
|
||||
|
||||
sprintf(s, "%s\\*", dir_path);
|
||||
h = FindFirstFileA(s, &fd);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
goto create_and_exit;
|
||||
sprintf(s, "%s\\*", dir_path);
|
||||
h = FindFirstFileA(s, &fd);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
goto create_and_exit;
|
||||
|
||||
do {
|
||||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
continue;
|
||||
if (is_valid_module_file(fd.cFileName))
|
||||
count++;
|
||||
} while (FindNextFileA(h, &fd));
|
||||
do {
|
||||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
continue;
|
||||
if (is_valid_module_file(fd.cFileName))
|
||||
count++;
|
||||
} while (FindNextFileA(h, &fd));
|
||||
|
||||
FindClose(h);
|
||||
FindClose(h);
|
||||
|
||||
if (count == 0)
|
||||
goto exit;
|
||||
if (count == 0)
|
||||
goto exit;
|
||||
|
||||
list = malloc(count * sizeof(*list));
|
||||
if (!list)
|
||||
goto exit;
|
||||
list = malloc(count * sizeof(*list));
|
||||
if (!list)
|
||||
goto exit;
|
||||
|
||||
h = FindFirstFileA(s, &fd);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
goto exit;
|
||||
h = FindFirstFileA(s, &fd);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
goto exit;
|
||||
|
||||
do {
|
||||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
continue;
|
||||
if (is_valid_module_file(fd.cFileName) && i < count) {
|
||||
name_len = strlen(fd.cFileName);
|
||||
if (name_len >= STK_PATH_MAX)
|
||||
name_len = STK_PATH_MAX - 1;
|
||||
do {
|
||||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
continue;
|
||||
if (is_valid_module_file(fd.cFileName) && i < count) {
|
||||
name_len = strlen(fd.cFileName);
|
||||
if (name_len >= STK_PATH_MAX)
|
||||
name_len = STK_PATH_MAX - 1;
|
||||
|
||||
memcpy(list[i], fd.cFileName, name_len);
|
||||
list[i][name_len] = '\0';
|
||||
i++;
|
||||
}
|
||||
} while (FindNextFileA(h, &fd));
|
||||
memcpy(list[i], fd.cFileName, name_len);
|
||||
list[i][name_len] = '\0';
|
||||
i++;
|
||||
}
|
||||
} while (FindNextFileA(h, &fd));
|
||||
|
||||
FindClose(h);
|
||||
goto exit;
|
||||
FindClose(h);
|
||||
goto exit;
|
||||
|
||||
create_and_exit:
|
||||
platform_mkdir(dir_path);
|
||||
exit:
|
||||
*out_count = i;
|
||||
return list;
|
||||
create_and_exit:
|
||||
platform_mkdir(dir_path);
|
||||
exit:
|
||||
*out_count = i;
|
||||
return list;
|
||||
#else
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
struct stat st;
|
||||
char f[STK_PATH_MAX_OS];
|
||||
DIR *d;
|
||||
struct dirent *e;
|
||||
struct stat st;
|
||||
char f[STK_PATH_MAX_OS];
|
||||
|
||||
d = opendir(dir_path);
|
||||
if (!d)
|
||||
goto create_and_exit;
|
||||
d = opendir(dir_path);
|
||||
if (!d)
|
||||
goto create_and_exit;
|
||||
|
||||
count_loop:
|
||||
e = readdir(d);
|
||||
if (!e)
|
||||
goto count_done;
|
||||
count_loop:
|
||||
e = readdir(d);
|
||||
if (!e)
|
||||
goto count_done;
|
||||
|
||||
sprintf(f, "%s/%s", dir_path, e->d_name);
|
||||
if (!is_valid_module_file(e->d_name))
|
||||
goto count_loop;
|
||||
sprintf(f, "%s/%s", dir_path, e->d_name);
|
||||
if (!is_valid_module_file(e->d_name))
|
||||
goto count_loop;
|
||||
|
||||
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
||||
goto count_loop;
|
||||
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
||||
goto count_loop;
|
||||
|
||||
count++;
|
||||
goto count_loop;
|
||||
count++;
|
||||
goto count_loop;
|
||||
|
||||
count_done:
|
||||
if (count == 0)
|
||||
goto close_and_exit;
|
||||
count_done:
|
||||
if (count == 0)
|
||||
goto close_and_exit;
|
||||
|
||||
rewinddir(d);
|
||||
list = malloc(count * sizeof(*list));
|
||||
if (!list)
|
||||
goto close_and_exit;
|
||||
rewinddir(d);
|
||||
list = malloc(count * sizeof(*list));
|
||||
if (!list)
|
||||
goto close_and_exit;
|
||||
|
||||
fill_loop:
|
||||
e = readdir(d);
|
||||
if (!e || i >= count)
|
||||
goto close_and_exit;
|
||||
fill_loop:
|
||||
e = readdir(d);
|
||||
if (!e || i >= count)
|
||||
goto close_and_exit;
|
||||
|
||||
sprintf(f, "%s/%s", dir_path, e->d_name);
|
||||
if (!is_valid_module_file(e->d_name))
|
||||
goto fill_loop;
|
||||
sprintf(f, "%s/%s", dir_path, e->d_name);
|
||||
if (!is_valid_module_file(e->d_name))
|
||||
goto fill_loop;
|
||||
|
||||
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
||||
goto fill_loop;
|
||||
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
|
||||
goto fill_loop;
|
||||
|
||||
name_len = strlen(e->d_name);
|
||||
if (name_len >= STK_PATH_MAX) {
|
||||
name_len = STK_PATH_MAX - 1;
|
||||
}
|
||||
memcpy(list[i++], e->d_name, name_len);
|
||||
list[i - 1][name_len] = '\0';
|
||||
goto fill_loop;
|
||||
name_len = strlen(e->d_name);
|
||||
if (name_len >= STK_PATH_MAX) {
|
||||
name_len = STK_PATH_MAX - 1;
|
||||
}
|
||||
memcpy(list[i++], e->d_name, name_len);
|
||||
list[i - 1][name_len] = '\0';
|
||||
goto fill_loop;
|
||||
|
||||
create_and_exit:
|
||||
platform_mkdir(dir_path);
|
||||
*out_count = 0;
|
||||
return NULL;
|
||||
create_and_exit:
|
||||
platform_mkdir(dir_path);
|
||||
*out_count = 0;
|
||||
return NULL;
|
||||
|
||||
close_and_exit:
|
||||
closedir(d);
|
||||
*out_count = i;
|
||||
return list;
|
||||
close_and_exit:
|
||||
closedir(d);
|
||||
*out_count = i;
|
||||
return list;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__linux__) && !defined(_WIN32)
|
||||
static void update_watches(platform_watch_context_t *ctx)
|
||||
@@ -665,7 +658,7 @@ stk_module_event_t *platform_directory_watch_check(
|
||||
int fd = (int)(long)handle;
|
||||
char buf[STK_EVENT_BUFFER];
|
||||
ssize_t len;
|
||||
size_t index = 0, count = 0, i;
|
||||
size_t index = 0, count = 0, i, write_index;
|
||||
stk_module_event_t *evs;
|
||||
char *ptr, *end;
|
||||
struct inotify_event *e;
|
||||
@@ -763,6 +756,19 @@ stk_module_event_t *platform_directory_watch_check(
|
||||
}
|
||||
}
|
||||
|
||||
write_index = 0;
|
||||
for (i = 0; i < index; ++i) {
|
||||
if (evs[i] != -1) {
|
||||
if (write_index != i) {
|
||||
evs[write_index] = evs[i];
|
||||
memmove((*file_list)[write_index],
|
||||
(*file_list)[i], STK_PATH_MAX);
|
||||
}
|
||||
write_index++;
|
||||
}
|
||||
}
|
||||
index = write_index;
|
||||
|
||||
*out_count = index;
|
||||
return evs;
|
||||
|
||||
@@ -1000,3 +1006,25 @@ no_change:
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void platform_get_timestamp(char *buffer, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEMTIME st;
|
||||
GetLocalTime(&st);
|
||||
sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d", st.wYear,
|
||||
st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
|
||||
st.wMilliseconds);
|
||||
#else
|
||||
struct timeval tv;
|
||||
struct tm *tm_info;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
tm_info = localtime(&tv.tv_sec);
|
||||
|
||||
sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
|
||||
tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
|
||||
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
|
||||
(int)(tv.tv_usec / 1000));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "stk.h"
|
||||
#include "platform.h"
|
||||
#include "stk_log.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -16,7 +15,7 @@ extern char (*stk_module_ids)[STK_MOD_ID_BUFFER];
|
||||
|
||||
extern size_t module_count;
|
||||
|
||||
uint8_t stk_initialized = 0;
|
||||
unsigned char stk_flags = STK_FLAG_LOGGING_ENABLED;
|
||||
|
||||
static char stk_mod_dir[STK_PATH_MAX_OS] = "mods";
|
||||
static char stk_tmp_name[STK_MOD_ID_BUFFER] = ".tmp";
|
||||
@@ -30,18 +29,18 @@ void platform_directory_watch_stop(void *handle);
|
||||
stk_module_event_t *platform_directory_watch_check(
|
||||
void *handle, char (**file_list)[STK_PATH_MAX], size_t *out_count,
|
||||
char (*loaded_module_ids)[STK_MOD_ID_BUFFER], const size_t loaded_count);
|
||||
uint8_t platform_mkdir(const char *path);
|
||||
uint8_t platform_copy_file(const char *from, const char *to);
|
||||
uint8_t platform_remove_dir(const char *path);
|
||||
unsigned char platform_mkdir(const char *path);
|
||||
unsigned char platform_copy_file(const char *from, const char *to);
|
||||
unsigned char platform_remove_dir(const char *path);
|
||||
|
||||
void extract_module_id(const char *path, char *out_id);
|
||||
int is_mod_loaded(const char *module_id);
|
||||
|
||||
size_t stk_module_count(void);
|
||||
uint8_t stk_module_load(const char *path, int index);
|
||||
uint8_t stk_module_load_init(const char *path, int index);
|
||||
uint8_t stk_module_init_memory(size_t capacity);
|
||||
uint8_t stk_module_realloc_memory(size_t new_capacity);
|
||||
unsigned char stk_module_load(const char *path, int index);
|
||||
unsigned char stk_module_load_init(const char *path, int index);
|
||||
unsigned char stk_module_init_memory(size_t capacity);
|
||||
unsigned char stk_module_realloc_memory(size_t new_capacity);
|
||||
void stk_module_unload(size_t index);
|
||||
void stk_module_unload_all(void);
|
||||
|
||||
@@ -70,7 +69,7 @@ static const char *stk_error_string(int error_code)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t stk_init(void)
|
||||
unsigned char stk_init(void)
|
||||
{
|
||||
char (*files)[STK_PATH_MAX] = NULL;
|
||||
size_t file_count, i, successful_loads = 0;
|
||||
@@ -87,7 +86,7 @@ uint8_t stk_init(void)
|
||||
if (test_scan)
|
||||
free(test_scan);
|
||||
if (!test_scan && test_count == 0) {
|
||||
stk_log(stderr,
|
||||
stk_log(STK_LOG_ERROR,
|
||||
"FATAL: Cannot create temp directory: %s",
|
||||
stk_tmp_dir);
|
||||
return STK_INIT_TMPDIR_ERROR;
|
||||
@@ -97,7 +96,7 @@ uint8_t stk_init(void)
|
||||
files = platform_directory_init_scan(stk_mod_dir, &file_count);
|
||||
|
||||
if (file_count > 0 && stk_module_init_memory(file_count) != 0) {
|
||||
stk_log(stderr, "FATAL: Memory allocation failed");
|
||||
stk_log(STK_LOG_ERROR, "FATAL: Memory allocation failed");
|
||||
return STK_INIT_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
@@ -110,7 +109,8 @@ uint8_t stk_init(void)
|
||||
|
||||
if (platform_copy_file(full_path, tmp_path) !=
|
||||
STK_PLATFORM_OPERATION_SUCCESS) {
|
||||
stk_log(stderr, "Failed to copy %s to temp directory",
|
||||
stk_log(STK_LOG_ERROR,
|
||||
"Failed to copy %s to temp directory",
|
||||
files[i]);
|
||||
continue;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ uint8_t stk_init(void)
|
||||
load_result = stk_module_load_init(tmp_path, successful_loads);
|
||||
|
||||
if (load_result != STK_MOD_INIT_SUCCESS) {
|
||||
stk_log(stderr, "Failed to load module %s: %s",
|
||||
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
|
||||
files[i], stk_error_string(load_result));
|
||||
} else {
|
||||
successful_loads++;
|
||||
@@ -134,17 +134,18 @@ uint8_t stk_init(void)
|
||||
scanned:
|
||||
watch_handle = platform_directory_watch_start(stk_mod_dir);
|
||||
if (!watch_handle) {
|
||||
stk_log(stderr, "FATAL: Cannot start directory watch on %s",
|
||||
stk_log(STK_LOG_ERROR,
|
||||
"FATAL: Cannot start directory watch on %s",
|
||||
stk_mod_dir);
|
||||
stk_module_unload_all();
|
||||
return STK_INIT_WATCH_ERROR;
|
||||
}
|
||||
|
||||
stk_log(stdout, "stk v%s initialized! Loaded %lu mod%s from %s/",
|
||||
stk_log(STK_LOG_INFO, "stk v%s initialized! Loaded %lu mod%s from %s/",
|
||||
STK_VERSION_STRING, module_count, module_count != 1 ? "s" : "",
|
||||
stk_mod_dir);
|
||||
|
||||
stk_initialized = 1;
|
||||
stk_flags |= STK_FLAG_INITIALIZED;
|
||||
return STK_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -159,12 +160,13 @@ void stk_shutdown(void)
|
||||
|
||||
if (platform_remove_dir(stk_tmp_dir) !=
|
||||
STK_PLATFORM_OPERATION_SUCCESS) {
|
||||
stk_log(stderr, "Warning: failed to remove temp directory %s",
|
||||
stk_log(STK_LOG_WARN,
|
||||
"Warning: failed to remove temp directory %s",
|
||||
stk_tmp_dir);
|
||||
}
|
||||
|
||||
stk_initialized = 0;
|
||||
stk_log(stdout, "stk shutdown");
|
||||
stk_flags &= ~STK_FLAG_INITIALIZED;
|
||||
stk_log(STK_LOG_INFO, "stk shutdown");
|
||||
}
|
||||
|
||||
size_t stk_poll(void)
|
||||
@@ -172,8 +174,7 @@ size_t stk_poll(void)
|
||||
char (*file_list)[STK_PATH_MAX] = NULL;
|
||||
stk_module_event_t *events = NULL;
|
||||
size_t i, file_count = 0, reload_count = 0, load_count = 0,
|
||||
unload_count = 0, reload_index = 0, load_index = 0,
|
||||
unload_index = 0;
|
||||
unload_count = 0;
|
||||
int *reloaded_mod_indices = NULL, *reloaded_mod_file_indices = NULL,
|
||||
*unloaded_mod_indices = NULL, *loaded_mod_indices = NULL;
|
||||
size_t remaining_loads, new_capacity, holes_to_fill;
|
||||
@@ -208,20 +209,31 @@ size_t stk_poll(void)
|
||||
unloaded_mod_indices = malloc(unload_count * sizeof(int));
|
||||
loaded_mod_indices = malloc(load_count * sizeof(int));
|
||||
|
||||
reload_count = 0;
|
||||
unload_count = 0;
|
||||
load_count = 0;
|
||||
|
||||
for (i = 0; i < file_count; ++i) {
|
||||
int mod_index;
|
||||
extract_module_id(file_list[i], mod_id);
|
||||
switch (events[i]) {
|
||||
case STK_MOD_LOAD:
|
||||
loaded_mod_indices[load_index++] = i;
|
||||
loaded_mod_indices[load_count++] = i;
|
||||
break;
|
||||
case STK_MOD_RELOAD:
|
||||
reloaded_mod_file_indices[reload_index] = i;
|
||||
reloaded_mod_indices[reload_index++] =
|
||||
is_mod_loaded(mod_id);
|
||||
mod_index = is_mod_loaded(mod_id);
|
||||
if (mod_index >= 0) {
|
||||
reloaded_mod_file_indices[reload_count] = i;
|
||||
reloaded_mod_indices[reload_count] = mod_index;
|
||||
reload_count++;
|
||||
}
|
||||
break;
|
||||
case STK_MOD_UNLOAD:
|
||||
unloaded_mod_indices[unload_index++] =
|
||||
is_mod_loaded(mod_id);
|
||||
mod_index = is_mod_loaded(mod_id);
|
||||
if (mod_index >= 0) {
|
||||
unloaded_mod_indices[unload_count] = mod_index;
|
||||
unload_count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -242,9 +254,6 @@ begin_operations:
|
||||
for (i = 0; i < unload_count; ++i)
|
||||
stk_module_unload(unloaded_mod_indices[i]);
|
||||
|
||||
for (i = 0; i < reload_count; ++i)
|
||||
stk_module_unload(reloaded_mod_indices[i]);
|
||||
|
||||
for (i = 0; i < reload_count; ++i) {
|
||||
int file_index = reloaded_mod_file_indices[i];
|
||||
int mod_index = reloaded_mod_indices[i];
|
||||
@@ -256,14 +265,16 @@ begin_operations:
|
||||
|
||||
if (platform_copy_file(full_path, tmp_path) !=
|
||||
STK_PLATFORM_OPERATION_SUCCESS) {
|
||||
stk_log(stderr, "Failed to copy %s for reload",
|
||||
stk_log(STK_LOG_ERROR, "Failed to copy %s for reload",
|
||||
file_list[file_index]);
|
||||
continue;
|
||||
}
|
||||
|
||||
stk_module_unload(mod_index);
|
||||
|
||||
load_result = stk_module_load(tmp_path, mod_index);
|
||||
if (load_result != STK_MOD_INIT_SUCCESS) {
|
||||
stk_log(stderr, "Failed to reload module %s: %s",
|
||||
stk_log(STK_LOG_ERROR, "Failed to reload module %s: %s",
|
||||
file_list[file_index],
|
||||
stk_error_string(load_result));
|
||||
}
|
||||
@@ -281,14 +292,14 @@ begin_operations:
|
||||
|
||||
if (platform_copy_file(full_path, tmp_path) !=
|
||||
STK_PLATFORM_OPERATION_SUCCESS) {
|
||||
stk_log(stderr, "Failed to copy %s for loading",
|
||||
stk_log(STK_LOG_ERROR, "Failed to copy %s for loading",
|
||||
file_list[file_index]);
|
||||
continue;
|
||||
}
|
||||
|
||||
load_result = stk_module_load(tmp_path, target_index);
|
||||
if (load_result != STK_MOD_INIT_SUCCESS) {
|
||||
stk_log(stderr, "Failed to load module %s: %s",
|
||||
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
|
||||
file_list[file_index],
|
||||
stk_error_string(load_result));
|
||||
}
|
||||
@@ -313,7 +324,7 @@ append_modules:
|
||||
|
||||
if (platform_copy_file(full_path, tmp_path) !=
|
||||
STK_PLATFORM_OPERATION_SUCCESS) {
|
||||
stk_log(stderr, "Failed to copy %s for loading",
|
||||
stk_log(STK_LOG_ERROR, "Failed to copy %s for loading",
|
||||
file_list[file_index]);
|
||||
continue;
|
||||
}
|
||||
@@ -321,7 +332,7 @@ append_modules:
|
||||
load_result = stk_module_load(tmp_path, module_count +
|
||||
successful_appends);
|
||||
if (load_result != STK_MOD_INIT_SUCCESS) {
|
||||
stk_log(stderr, "Failed to load module %s: %s",
|
||||
stk_log(STK_LOG_ERROR, "Failed to load module %s: %s",
|
||||
file_list[file_index],
|
||||
stk_error_string(load_result));
|
||||
} else {
|
||||
@@ -372,7 +383,7 @@ finish_poll:
|
||||
|
||||
void stk_set_mod_dir(const char *path)
|
||||
{
|
||||
if (!path || stk_initialized)
|
||||
if (!path || (stk_flags & STK_FLAG_INITIALIZED))
|
||||
return;
|
||||
|
||||
strncpy(stk_mod_dir, path, STK_PATH_MAX_OS - 1);
|
||||
@@ -390,7 +401,7 @@ void stk_set_mod_dir(const char *path)
|
||||
|
||||
void stk_set_tmp_dir_name(const char *name)
|
||||
{
|
||||
if (!name || stk_initialized)
|
||||
if (!name || (stk_flags & STK_FLAG_INITIALIZED))
|
||||
return;
|
||||
|
||||
strncpy(stk_tmp_name, name, STK_MOD_ID_BUFFER - 1);
|
||||
@@ -403,3 +414,16 @@ void stk_set_tmp_dir_name(const char *name)
|
||||
strncat(stk_tmp_dir, stk_tmp_name,
|
||||
STK_PATH_MAX_OS - strlen(stk_tmp_dir) - 1);
|
||||
}
|
||||
|
||||
void stk_set_logging_enabled(unsigned char enabled)
|
||||
{
|
||||
if (enabled)
|
||||
stk_flags |= STK_FLAG_LOGGING_ENABLED;
|
||||
else
|
||||
stk_flags &= ~STK_FLAG_LOGGING_ENABLED;
|
||||
}
|
||||
|
||||
unsigned char stk_is_logging_enabled(void)
|
||||
{
|
||||
return (stk_flags & STK_FLAG_LOGGING_ENABLED) != 0;
|
||||
}
|
||||
|
||||
+77
-5
@@ -1,14 +1,86 @@
|
||||
#include "stk_log.h"
|
||||
#include "stk.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void stk_log(FILE *fp, const char *fmt, ...)
|
||||
#define STK_LOG_TIMESTAMP_BUFFER 32
|
||||
|
||||
extern unsigned char stk_flags;
|
||||
void platform_get_timestamp(char *buffer, size_t size);
|
||||
|
||||
static FILE *log_output = NULL;
|
||||
static char log_prefix[STK_LOG_PREFIX_BUFFER] = "stk";
|
||||
static stk_log_level_t min_log_level = STK_LOG_INFO;
|
||||
|
||||
static const char *get_level_string(stk_log_level_t level)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char *level_str = "";
|
||||
switch (level) {
|
||||
case STK_LOG_ERROR:
|
||||
level_str = "ERROR";
|
||||
break;
|
||||
case STK_LOG_WARN:
|
||||
level_str = "WARN";
|
||||
break;
|
||||
case STK_LOG_INFO:
|
||||
level_str = "INFO";
|
||||
break;
|
||||
case STK_LOG_DEBUG:
|
||||
level_str = "DEBUG";
|
||||
break;
|
||||
}
|
||||
|
||||
vfprintf(fp, fmt, args);
|
||||
fputc('\n', fp);
|
||||
return level_str;
|
||||
}
|
||||
|
||||
void stk_set_log_output(FILE *fp)
|
||||
{
|
||||
if (fp == NULL)
|
||||
stk_flags &= ~STK_FLAG_LOGGING_ENABLED;
|
||||
|
||||
log_output = fp;
|
||||
}
|
||||
|
||||
void stk_set_log_prefix(const char *prefix)
|
||||
{
|
||||
if (!prefix) {
|
||||
log_prefix[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
strncpy(log_prefix, prefix, STK_LOG_PREFIX_BUFFER - 1);
|
||||
log_prefix[STK_LOG_PREFIX_BUFFER - 1] = '\0';
|
||||
}
|
||||
|
||||
void stk_set_log_level(stk_log_level_t level) { min_log_level = level; }
|
||||
|
||||
void stk_log(stk_log_level_t level, const char *fmt, ...)
|
||||
{
|
||||
FILE *output;
|
||||
const char *level_str;
|
||||
char timestamp[STK_LOG_TIMESTAMP_BUFFER];
|
||||
va_list args;
|
||||
|
||||
if (!(stk_flags & STK_FLAG_LOGGING_ENABLED))
|
||||
return;
|
||||
|
||||
if (level < min_log_level)
|
||||
return;
|
||||
|
||||
output = log_output ? log_output : stdout;
|
||||
level_str = get_level_string(level);
|
||||
platform_get_timestamp(timestamp, sizeof(timestamp));
|
||||
|
||||
if (log_prefix[0] != '\0')
|
||||
fprintf(output, "%s [%s] [%s] ", timestamp, log_prefix,
|
||||
level_str);
|
||||
else
|
||||
fprintf(output, "%s [%s] ", timestamp, level_str);
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(output, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
fputc('\n', output);
|
||||
}
|
||||
|
||||
+5
-4
@@ -31,8 +31,8 @@ void inthand(int signum)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
uint8_t init_result;
|
||||
uint64_t iterations = 0;
|
||||
unsigned char init_result;
|
||||
size_t iterations = 0;
|
||||
|
||||
printf("stk test - CTRL+C to exit\n");
|
||||
|
||||
@@ -55,12 +55,13 @@ int main(int argc, char **argv)
|
||||
while (!stop) {
|
||||
size_t events = stk_poll();
|
||||
if (events > 0)
|
||||
printf("Poll: %lu module event(s) detected\n", (unsigned long) events);
|
||||
printf("Poll: %lu module event(s) detected\n",
|
||||
(unsigned long)events);
|
||||
|
||||
iterations++;
|
||||
if (iterations % 5 == 0) {
|
||||
printf("Still running... (iteration %lu)\n",
|
||||
(unsigned long) iterations);
|
||||
(unsigned long)iterations);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
Sleep(1000);
|
||||
|
||||
Reference in New Issue
Block a user