Files
stk/src/platform.c
T

1013 lines
20 KiB
C

#include "stk.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <sys/file.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);
void extract_module_id(const char *path, char *out_id);
static uint8_t is_file_ready(const char *dir_path, const char *filename)
{
char full_path[STK_PATH_MAX_OS];
#ifdef _WIN32
DWORD size;
HANDLE h;
sprintf(full_path, "%s\\%s", dir_path, filename);
h = CreateFileA(full_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return 0;
size = GetFileSize(h, NULL);
CloseHandle(h);
if (size == INVALID_FILE_SIZE || size < 1024)
return 0;
return 1;
#else
int fd;
struct stat st;
sprintf(full_path, "%s/%s", dir_path, filename);
if (stat(full_path, &st) != 0)
return 0;
if (st.st_size < 1024)
return 0;
fd = open(full_path, O_RDONLY);
if (fd < 0)
return 0;
if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
close(fd);
return 0;
}
flock(fd, LOCK_UN);
close(fd);
return 1;
#endif
}
#ifndef __linux__
typedef struct {
char filename[STK_PATH_MAX];
#ifdef _WIN32
FILETIME mtime;
#else
time_t mtime;
#endif
} platform_snapshot_t;
typedef struct {
char path[STK_PATH_MAX];
platform_snapshot_t *snaps;
size_t count;
union {
#ifdef _WIN32
HANDLE change_handle;
#else
struct {
int kq;
int dir_fd;
int *file_fds;
size_t file_fd_count;
} k;
#endif
} watch;
} platform_watch_context_t;
#endif
uint8_t platform_mkdir(const char *path)
{
#ifdef _WIN32
return CreateDirectoryA(path, NULL) ? STK_PLATFORM_OPERATION_SUCCESS
: STK_PLATFORM_MKDIR_ERROR;
#else
return mkdir(path, 0755) == 0 ? STK_PLATFORM_OPERATION_SUCCESS
: STK_PLATFORM_MKDIR_ERROR;
#endif
}
int platform_remove_file(const char *path)
{
#ifdef _WIN32
return DeleteFileA(path) ? STK_PLATFORM_OPERATION_SUCCESS
: STK_PLATFORM_REMOVE_FILE_ERROR;
#else
return unlink(path) == 0 ? STK_PLATFORM_OPERATION_SUCCESS
: STK_PLATFORM_REMOVE_FILE_ERROR;
#endif
}
uint8_t platform_copy_file(const char *from, const char *to)
{
char buf[STK_PATH_MAX_OS];
int ret = STK_PLATFORM_FILE_COPY_ERROR;
#ifdef _WIN32
sprintf(buf, "%s.tmp", to);
if (CopyFileA(from, buf, FALSE)) {
if (MoveFileExA(buf, to, MOVEFILE_REPLACE_EXISTING))
ret = 0;
else
DeleteFileA(buf);
}
#else
FILE *src = NULL, *dst = NULL;
char tmp_path[STK_PATH_MAX_OS];
size_t n;
char dir_path[STK_PATH_MAX_OS];
const char *filename;
filename = strrchr(from, '/');
if (filename) {
size_t dir_len = filename - from;
strncpy(dir_path, from, dir_len);
dir_path[dir_len] = '\0';
filename++;
} else {
strcpy(dir_path, ".");
filename = from;
}
if (!is_file_ready(dir_path, filename))
goto done;
sprintf(tmp_path, "%s.tmp", to);
src = fopen(from, "rb");
if (!src)
goto cleanup;
dst = fopen(tmp_path, "wb");
if (!dst)
goto cleanup;
while ((n = fread(buf, 1, sizeof(buf), src)) > 0)
fwrite(buf, 1, n, dst);
if (src)
fclose(src);
if (dst)
fclose(dst);
src = NULL;
dst = NULL;
if (rename(tmp_path, to) == 0)
ret = STK_PLATFORM_OPERATION_SUCCESS;
else
unlink(tmp_path);
goto done;
cleanup:
if (src)
fclose(src);
if (dst)
fclose(dst);
unlink(tmp_path);
done:
#endif
return ret;
}
uint8_t platform_remove_dir(const char *path)
{
#ifdef _WIN32
WIN32_FIND_DATAA fd;
HANDLE h;
char s[STK_PATH_MAX_OS], f[STK_PATH_MAX_OS];
sprintf(s, "%s\\*", path);
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE)
goto remove_dir;
do {
if (strcmp(fd.cFileName, ".") == 0 ||
strcmp(fd.cFileName, "..") == 0)
continue;
sprintf(f, "%s\\%s", path, fd.cFileName);
DeleteFileA(f);
} while (FindNextFileA(h, &fd));
FindClose(h);
remove_dir:
return RemoveDirectoryA(path) ? STK_PLATFORM_OPERATION_SUCCESS
: STK_PLATFORM_REMOVE_DIR_ERROR;
#else
DIR *dir;
struct dirent *entry;
char filepath[STK_PATH_MAX_OS];
dir = opendir(path);
if (!dir)
return STK_PLATFORM_REMOVE_DIR_ERROR;
loop:
entry = readdir(dir);
if (!entry)
goto loop_end;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
goto loop;
sprintf(filepath, "%s/%s", path, entry->d_name);
unlink(filepath);
goto loop;
loop_end:
closedir(dir);
return rmdir(path) == 0 ? STK_PLATFORM_OPERATION_SUCCESS
: STK_PLATFORM_REMOVE_DIR_ERROR;
#endif
}
void *platform_load_library(const char *path)
{
#ifdef _WIN32
return (void *)LoadLibraryA(path);
#else
return dlopen(path, RTLD_NOW);
#endif
}
void platform_unload_library(void *h)
{
#ifdef _WIN32
FreeLibrary((HMODULE)h);
#else
dlclose(h);
#endif
}
void *platform_get_symbol(void *h, const char *s)
{
#ifdef _WIN32
return (void *)(intptr_t)GetProcAddress((HMODULE)h, s);
#else
return dlsym(h, 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;
#ifdef _WIN32
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;
do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
continue;
if (is_valid_module_file(fd.cFileName))
count++;
} while (FindNextFileA(h, &fd));
FindClose(h);
if (count == 0)
goto exit;
list = malloc(count * sizeof(*list));
if (!list)
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;
memcpy(list[i], fd.cFileName, name_len);
list[i][name_len] = '\0';
i++;
}
} while (FindNextFileA(h, &fd));
FindClose(h);
goto exit;
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];
d = opendir(dir_path);
if (!d)
goto create_and_exit;
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;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto count_loop;
count++;
goto count_loop;
count_done:
if (count == 0)
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;
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;
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;
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)
{
struct kevent ev;
DIR *d;
struct dirent *e;
char f[STK_PATH_MAX_OS];
size_t i, count = 0;
int fd;
int *new_fds = NULL;
for (i = 0; i < ctx->watch.k.file_fd_count; i++)
close(ctx->watch.k.file_fds[i]);
free(ctx->watch.k.file_fds);
ctx->watch.k.file_fds = NULL;
ctx->watch.k.file_fd_count = 0;
EV_SET(&ev, ctx->watch.k.dir_fd, EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_WRITE | NOTE_DELETE | NOTE_RENAME, 0, NULL);
kevent(ctx->watch.k.kq, &ev, 1, NULL, 0, NULL);
d = opendir(ctx->path);
if (!d)
return;
count_loop:
e = readdir(d);
if (!e)
goto count_done;
if (is_valid_module_file(e->d_name))
count++;
goto count_loop;
count_done:
if (count == 0)
goto cleanup;
new_fds = malloc(count * sizeof(int));
if (!new_fds)
goto cleanup;
rewinddir(d);
i = 0;
scan_loop:
e = readdir(d);
if (!e || i >= count)
goto scan_done;
if (!is_valid_module_file(e->d_name))
goto scan_loop;
sprintf(f, "%s/%s", ctx->path, e->d_name);
fd = open(f, O_RDONLY);
if (fd < 0)
goto scan_loop;
new_fds[i++] = fd;
EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_WRITE | NOTE_ATTRIB, 0, NULL);
kevent(ctx->watch.k.kq, &ev, 1, NULL, 0, NULL);
goto scan_loop;
scan_done:
ctx->watch.k.file_fds = new_fds;
ctx->watch.k.file_fd_count = i;
cleanup:
closedir(d);
}
#endif
void *platform_directory_watch_start(const char *path)
{
#ifdef __linux__
int fd = inotify_init1(IN_NONBLOCK);
if (fd < 0)
return NULL;
inotify_add_watch(
fd, path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM);
return (void *)(long)fd;
#else
#ifdef _WIN32
WIN32_FIND_DATAA fd;
HANDLE h;
char s[STK_PATH_MAX_OS];
size_t count = 0, i = 0, name_len;
#else
DIR *d;
struct dirent *e;
struct stat st;
char f[STK_PATH_MAX_OS];
size_t count = 0, i = 0;
#endif
platform_watch_context_t *ctx =
calloc(1, sizeof(platform_watch_context_t));
if (!ctx)
return NULL;
strncpy(ctx->path, path, STK_PATH_MAX - 1);
#ifdef _WIN32
ctx->watch.change_handle =
CreateFileA(path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (ctx->watch.change_handle == INVALID_HANDLE_VALUE)
goto error_cleanup;
sprintf(s, "%s\\*", path);
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE)
goto error_cleanup;
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName))
count++;
} while (FindNextFileA(h, &fd));
FindClose(h);
if (count == 0)
goto done;
ctx->snaps = malloc(count * sizeof(platform_snapshot_t));
if (!ctx->snaps)
goto error_cleanup;
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE)
goto error_cleanup;
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
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(ctx->snaps[i].filename, fd.cFileName, name_len);
ctx->snaps[i].filename[name_len] = '\0';
ctx->snaps[i].mtime = fd.ftLastWriteTime;
i++;
}
} while (FindNextFileA(h, &fd));
FindClose(h);
ctx->count = i;
#else
ctx->watch.k.kq = kqueue();
ctx->watch.k.dir_fd = open(path, O_RDONLY);
d = opendir(path);
if (!d)
goto bsd_setup;
bsd_count_loop:
e = readdir(d);
if (!e)
goto bsd_count_done;
sprintf(f, "%s/%s", path, e->d_name);
if (!is_valid_module_file(e->d_name))
goto bsd_count_loop;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_count_loop;
count++;
goto bsd_count_loop;
bsd_count_done:
if (count == 0)
goto bsd_setup;
ctx->snaps = malloc(count * sizeof(platform_snapshot_t));
if (!ctx->snaps)
goto bsd_setup;
rewinddir(d);
bsd_scan_loop:
e = readdir(d);
if (!e || i >= count)
goto bsd_scan_done;
sprintf(f, "%s/%s", path, e->d_name);
if (!is_valid_module_file(e->d_name))
goto bsd_scan_loop;
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_scan_loop;
strncpy(ctx->snaps[i].filename, e->d_name, STK_PATH_MAX - 1);
ctx->snaps[i].mtime = st.st_mtime;
i++;
goto bsd_scan_loop;
bsd_scan_done:
closedir(d);
ctx->count = i;
bsd_setup:
update_watches(ctx);
#endif
#ifdef _WIN32
done:
#endif
return ctx;
#ifdef _WIN32
error_cleanup:
if (ctx) {
if (ctx->watch.change_handle != INVALID_HANDLE_VALUE)
CloseHandle(ctx->watch.change_handle);
free(ctx->snaps);
free(ctx);
}
return NULL;
#endif
#endif
}
void platform_directory_watch_stop(void *handle)
{
#if defined(__linux__)
if (handle)
close((int)(long)handle);
#else
#ifndef _WIN32
size_t i;
#endif
platform_watch_context_t *ctx = (platform_watch_context_t *)handle;
if (!ctx)
return;
#ifdef _WIN32
CloseHandle(ctx->watch.change_handle);
#else
for (i = 0; i < ctx->watch.k.file_fd_count; i++)
close(ctx->watch.k.file_fds[i]);
free(ctx->watch.k.file_fds);
close(ctx->watch.k.kq);
close(ctx->watch.k.dir_fd);
#endif
free(ctx->snaps);
free(ctx);
#endif
}
stk_module_event_t *platform_directory_watch_check(
void *handle, char (**file_list)[STK_PATH_MAX], size_t *out_count,
char (*loaded_ids)[STK_MOD_ID_BUFFER], const size_t loaded_count)
{
#if defined(__linux__)
int fd = (int)(long)handle;
char buf[STK_EVENT_BUFFER];
ssize_t len;
size_t index = 0, count = 0, i, write_index;
stk_module_event_t *evs;
char *ptr, *end;
struct inotify_event *e;
int event_type;
len = read(fd, buf, sizeof(buf));
if (len <= 0) {
*out_count = 0;
return NULL;
}
ptr = buf;
end = buf + len;
while (ptr < end) {
e = (struct inotify_event *)ptr;
if (e->len && is_valid_module_file(e->name))
count++;
ptr += sizeof(struct inotify_event) + e->len;
}
if (count == 0) {
*out_count = 0;
return NULL;
}
evs = malloc(count * sizeof(stk_module_event_t));
if (!evs) {
*out_count = 0;
return NULL;
}
*file_list = malloc(count * sizeof(**file_list));
if (!*file_list) {
free(evs);
*out_count = 0;
return NULL;
}
ptr = buf;
while (ptr < end && index < count) {
e = (struct inotify_event *)ptr;
if (e->len && is_valid_module_file(e->name)) {
if (e->mask & (IN_DELETE | IN_MOVED_FROM)) {
char *ptr2 =
ptr + sizeof(struct inotify_event) + e->len;
int has_create = 0;
while (ptr2 < end) {
struct inotify_event *e2 =
(struct inotify_event *)ptr2;
if (e2->len &&
strcmp(e->name, e2->name) == 0 &&
(e2->mask &
(IN_CLOSE_WRITE | IN_MOVED_TO))) {
has_create = 1;
break;
}
ptr2 += sizeof(struct inotify_event) +
e2->len;
}
if (has_create) {
ptr += sizeof(struct inotify_event) +
e->len;
continue;
}
}
strncpy((*file_list)[index], e->name, STK_PATH_MAX - 1);
event_type = STK_MOD_UNLOAD;
if (e->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) {
char event_module_name[STK_MOD_ID_BUFFER];
extract_module_id(e->name, event_module_name);
if (is_mod_loaded(event_module_name) >= 0)
event_type = STK_MOD_RELOAD;
else
event_type = STK_MOD_LOAD;
}
evs[index++] = event_type;
}
ptr += sizeof(struct inotify_event) + e->len;
}
for (i = 0; i < index; ++i) {
size_t j;
for (j = i + 1; j < index; ++j) {
if (strcmp((*file_list)[i], (*file_list)[j]) == 0) {
evs[i] = -1;
break;
}
}
}
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;
#else
platform_watch_context_t *ctx = (platform_watch_context_t *)handle;
platform_snapshot_t *new_snaps = NULL;
size_t new_count = 0, i, j, ev_index = 0, name_len;
stk_module_event_t *evs = NULL;
int found;
#ifdef _WIN32
WIN32_FIND_DATAA fd;
HANDLE h;
char s[STK_PATH_MAX_OS];
size_t count = 0;
sprintf(s, "%s\\*", ctx->path);
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE)
goto build_diff;
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName))
count++;
} while (FindNextFileA(h, &fd));
FindClose(h);
if (count == 0)
goto build_diff;
new_snaps = malloc(count * sizeof(platform_snapshot_t));
if (!new_snaps)
goto build_diff;
h = FindFirstFileA(s, &fd);
if (h == INVALID_HANDLE_VALUE) {
free(new_snaps);
new_snaps = NULL;
goto build_diff;
}
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
is_valid_module_file(fd.cFileName) && new_count < count) {
name_len = strlen(fd.cFileName);
if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy(new_snaps[new_count].filename, fd.cFileName,
name_len);
new_snaps[new_count].filename[name_len] = '\0';
new_snaps[new_count].mtime = fd.ftLastWriteTime;
new_count++;
}
} while (FindNextFileA(h, &fd));
FindClose(h);
#else
struct kevent kev;
struct timespec ts = {0, 0};
DIR *d;
struct dirent *e;
struct stat st;
char f[STK_PATH_MAX_OS];
size_t count = 0;
if (kevent(ctx->watch.k.kq, NULL, 0, &kev, 1, &ts) <= 0)
goto no_change;
d = opendir(ctx->path);
if (!d)
goto bsd_update;
bsd_count_loop:
e = readdir(d);
if (!e)
goto bsd_count_done;
if (!is_valid_module_file(e->d_name))
goto bsd_count_loop;
sprintf(f, "%s/%s", ctx->path, e->d_name);
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_count_loop;
count++;
goto bsd_count_loop;
bsd_count_done:
if (count == 0) {
closedir(d);
goto bsd_update;
}
new_snaps = malloc(count * sizeof(platform_snapshot_t));
if (!new_snaps) {
closedir(d);
goto bsd_update;
}
rewinddir(d);
bsd_snap_loop:
e = readdir(d);
if (!e || new_count >= count)
goto bsd_snap_done;
if (!is_valid_module_file(e->d_name))
goto bsd_snap_loop;
sprintf(f, "%s/%s", ctx->path, e->d_name);
if (stat(f, &st) != 0 || !S_ISREG(st.st_mode))
goto bsd_snap_loop;
strncpy(new_snaps[new_count].filename, e->d_name, STK_PATH_MAX - 1);
new_snaps[new_count].mtime = st.st_mtime;
new_count++;
goto bsd_snap_loop;
bsd_snap_done:
closedir(d);
bsd_update:
update_watches(ctx);
#endif
#ifdef _WIN32
build_diff:
#endif
evs = malloc((ctx->count + new_count + 1) * sizeof(stk_module_event_t));
*file_list = malloc((ctx->count + new_count + 1) * sizeof(**file_list));
if (!evs || !*file_list)
goto cleanup_error;
for (i = 0; i < ctx->count; i++) {
found = 0;
for (j = 0; j < new_count; j++) {
if (strcmp(ctx->snaps[i].filename,
new_snaps[j].filename) != 0)
continue;
found = 1;
#ifdef _WIN32
if (CompareFileTime(&ctx->snaps[i].mtime,
&new_snaps[j].mtime) != 0) {
if (is_file_ready(ctx->path,
new_snaps[j].filename)) {
name_len =
strlen(new_snaps[j].filename);
if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index],
new_snaps[j].filename, name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_RELOAD;
} else {
new_snaps[j].mtime =
ctx->snaps[i].mtime;
}
}
#else
if (ctx->snaps[i].mtime != new_snaps[j].mtime) {
if (is_file_ready(ctx->path,
new_snaps[j].filename)) {
name_len =
strlen(ctx->snaps[i].filename);
if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index],
ctx->snaps[i].filename,
name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_RELOAD;
} else {
new_snaps[j].mtime =
ctx->snaps[i].mtime;
}
}
#endif
break;
}
if (!found) {
name_len = strlen(ctx->snaps[i].filename);
if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index], ctx->snaps[i].filename,
name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_UNLOAD;
}
}
for (j = 0; j < new_count; j++) {
found = 0;
for (i = 0; i < ctx->count; i++) {
if (strcmp(new_snaps[j].filename,
ctx->snaps[i].filename) == 0) {
found = 1;
break;
}
}
if (!found) {
name_len = strlen(new_snaps[j].filename);
if (name_len >= STK_PATH_MAX)
name_len = STK_PATH_MAX - 1;
memcpy((*file_list)[ev_index], new_snaps[j].filename,
name_len);
(*file_list)[ev_index][name_len] = '\0';
evs[ev_index++] = STK_MOD_LOAD;
}
}
if (ev_index == 0)
goto cleanup_empty;
free(ctx->snaps);
ctx->snaps = new_snaps;
ctx->count = new_count;
*out_count = ev_index;
return evs;
cleanup_error:
if (evs)
free(evs);
if (*file_list)
free(*file_list);
cleanup_empty:
free(new_snaps);
#ifndef _WIN32
no_change:
#endif
*out_count = 0;
return NULL;
#endif
}